-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class AnyInstanceMethod < ClassMethod
-
-
1
def mock
-
stubbee.any_instance.mocha
-
end
-
-
1
def reset_mocha
-
stubbee.any_instance.reset_mocha
-
end
-
-
1
def hide_original_method
-
if method_exists?(method)
-
begin
-
@original_method = stubbee.instance_method(method)
-
if @original_method && @original_method.owner == stubbee
-
@original_visibility = :public
-
if stubbee.protected_instance_methods.include?(method)
-
@original_visibility = :protected
-
elsif stubbee.private_instance_methods.include?(method)
-
@original_visibility = :private
-
end
-
stubbee.send(:remove_method, method)
-
end
-
rescue NameError
-
# deal with nasties like ActiveRecord::Associations::AssociationProxy
-
end
-
end
-
end
-
-
1
def define_new_method
-
stubbee.class_eval(%{
-
def #{method}(*args, &block)
-
self.class.any_instance.mocha.method_missing(:#{method}, *args, &block)
-
end
-
}, __FILE__, __LINE__)
-
end
-
-
1
def remove_new_method
-
stubbee.send(:remove_method, method)
-
end
-
-
1
def restore_original_method
-
if @original_method && @original_method.owner == stubbee
-
stubbee.send(:define_method, method, @original_method)
-
stubbee.send(@original_visibility, method)
-
end
-
end
-
-
1
def method_exists?(method)
-
return true if stubbee.public_instance_methods(false).include?(method)
-
return true if stubbee.protected_instance_methods(false).include?(method)
-
return true if stubbee.private_instance_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers'
-
1
require 'mocha/hooks'
-
1
require 'mocha/mockery'
-
1
require 'mocha/sequence'
-
1
require 'mocha/object_methods'
-
1
require 'mocha/module_methods'
-
1
require 'mocha/class_methods'
-
-
1
module Mocha
-
-
# Methods added to +Test::Unit::TestCase+, +MiniTest::Unit::TestCase+ or equivalent.
-
1
module API
-
-
1
include ParameterMatchers
-
1
include Hooks
-
-
# @private
-
1
def self.included(mod)
-
1
Object.send(:include, Mocha::ObjectMethods)
-
1
Module.send(:include, Mocha::ModuleMethods)
-
1
Class.send(:include, Mocha::ClassMethods)
-
end
-
-
# Builds a new mock object
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup expectations.
-
# @return [Mock] a new mock object
-
#
-
# @overload def mock(name, &block)
-
# @overload def mock(expected_methods_vs_return_values = {}, &block)
-
# @overload def mock(name, expected_methods_vs_return_values = {}, &block)
-
#
-
# @example Using expected_methods_vs_return_values Hash to setup expectations.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor', :start => true, :stop => true)
-
# assert motor.start
-
# assert motor.stop
-
# # an error will be raised unless both Motor#start and Motor#stop have been called
-
# end
-
# @example Using the optional block to setup expectations & stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor') do
-
# expects(:start).with(100.rpm).returns(true)
-
# stubs(:stop).returns(true)
-
# end
-
# assert motor.start(100.rpm)
-
# assert motor.stop
-
# # an error will only be raised if Motor#start(100.rpm) has not been called
-
# end
-
1
def mock(*arguments, &block)
-
1
name = arguments.shift if arguments.first.is_a?(String)
-
1
expectations = arguments.shift || {}
-
1
mock = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
1
mock.expects(expectations)
-
1
mock
-
end
-
-
# Builds a new mock object
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup stubbed methods.
-
# @return [Mock] a new mock object
-
#
-
# @overload def stub(name, &block)
-
# @overload def stub(stubbed_methods_vs_return_values = {}, &block)
-
# @overload def stub(name, stubbed_methods_vs_return_values = {}, &block)
-
#
-
# @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor', :start => true, :stop => true)
-
# assert motor.start
-
# assert motor.stop
-
# # an error will not be raised even if either Motor#start or Motor#stop has not been called
-
# end
-
#
-
# @example Using the optional block to setup expectations & stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor') do
-
# expects(:start).with(100.rpm).returns(true)
-
# stubs(:stop).returns(true)
-
# end
-
# assert motor.start(100.rpm)
-
# assert motor.stop
-
# # an error will only be raised if Motor#start(100.rpm) has not been called
-
# end
-
1
def stub(*arguments, &block)
-
3
name = arguments.shift if arguments.first.is_a?(String)
-
3
expectations = arguments.shift || {}
-
3
stub = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
3
stub.stubs(expectations)
-
3
stub
-
end
-
-
# Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup stubbed methods.
-
# @return [Mock] a new mock object
-
#
-
# @overload def stub_everything(name, &block)
-
# @overload def stub_everything(stubbed_methods_vs_return_values = {}, &block)
-
# @overload def stub_everything(name, stubbed_methods_vs_return_values = {}, &block)
-
#
-
# @example Ignore invocations of irrelevant methods.
-
# def test_motor_stops
-
# motor = stub_everything('motor', :stop => true)
-
# assert_nil motor.irrelevant_method_1 # => no error raised
-
# assert_nil motor.irrelevant_method_2 # => no error raised
-
# assert motor.stop
-
# end
-
1
def stub_everything(*arguments, &block)
-
2
name = arguments.shift if arguments.first.is_a?(String)
-
2
expectations = arguments.shift || {}
-
2
stub = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
2
stub.stub_everything
-
2
stub.stubs(expectations)
-
2
stub
-
end
-
-
# Builds a new sequence which can be used to constrain the order in which expectations can occur.
-
#
-
# Specify that an expected invocation must occur within a named {Sequence} by using {Expectation#in_sequence}.
-
#
-
# @return [Sequence] a new sequence
-
#
-
# @see Expectation#in_sequence
-
#
-
# @example Ensure methods on egg are invoked in correct order.
-
# breakfast = sequence('breakfast')
-
#
-
# egg = mock('egg') do
-
# expects(:crack).in_sequence(breakfast)
-
# expects(:fry).in_sequence(breakfast)
-
# expects(:eat).in_sequence(breakfast)
-
# end
-
1
def sequence(name)
-
Sequence.new(name)
-
end
-
-
# Builds a new state machine which can be used to constrain the order in which expectations can occur.
-
#
-
# Specify the initial state of the state machine by using {StateMachine#starts_as}.
-
#
-
# Specify that an expected invocation should change the state of the state machine by using {Expectation#then}.
-
#
-
# Specify that an expected invocation should be constrained to occur within a particular +state+ by using {Expectation#when}.
-
#
-
# A test can contain multiple state machines.
-
#
-
# @return [StateMachine] a new state machine
-
#
-
# @see Expectation#then
-
# @see Expectation#when
-
# @see StateMachine
-
# @example Constrain expected invocations to occur in particular states.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio') do
-
# expects(:switch_on).then(power.is('on'))
-
# expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# expects(:adjust_volume).with(+5).when(power.is('on'))
-
# expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# expects(:adjust_volume).with(-5).when(power.is('on'))
-
# expects(:switch_off).then(power.is('off'))
-
# end
-
1
def states(name)
-
Mockery.instance.new_state_machine(name)
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ArgumentIterator
-
-
1
def initialize(argument)
-
193
@argument = argument
-
end
-
-
1
def each(&block)
-
193
if @argument.is_a?(Hash) then
-
6
@argument.each do |method_name, return_value|
-
block.call(method_name, return_value)
-
end
-
else
-
187
block.call(@argument)
-
end
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class BacktraceFilter
-
-
1
LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
-
-
1
def initialize(lib_directory = LIB_DIRECTORY)
-
@path_pattern = Regexp.new(lib_directory)
-
end
-
-
1
def filtered(backtrace)
-
backtrace.reject { |location| @path_pattern.match(File.expand_path(location)) }
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Cardinality
-
-
1
INFINITY = 1 / 0.0
-
-
1
class << self
-
-
1
def exactly(count)
-
102
new(count, count)
-
end
-
-
1
def at_least(count)
-
6
new(count, INFINITY)
-
end
-
-
1
def at_most(count)
-
new(0, count)
-
end
-
-
1
def times(range_or_count)
-
case range_or_count
-
when Range then new(range_or_count.first, range_or_count.last)
-
else new(range_or_count, range_or_count)
-
end
-
end
-
-
end
-
-
1
def initialize(required, maximum)
-
108
@required, @maximum = required, maximum
-
end
-
-
1
def invocations_allowed?(invocation_count)
-
99
invocation_count < maximum
-
end
-
-
1
def satisfied?(invocations_so_far)
-
invocations_so_far >= required
-
end
-
-
1
def needs_verifying?
-
!allowed_any_number_of_times?
-
end
-
-
1
def verified?(invocation_count)
-
100
(invocation_count >= required) && (invocation_count <= maximum)
-
end
-
-
1
def allowed_any_number_of_times?
-
required == 0 && infinite?(maximum)
-
end
-
-
1
def used?(invocation_count)
-
(invocation_count > 0) || (maximum == 0)
-
end
-
-
1
def mocha_inspect
-
if allowed_any_number_of_times?
-
"allowed any number of times"
-
else
-
if required == 0 && maximum == 0
-
"expected never"
-
elsif required == maximum
-
"expected exactly #{times(required)}"
-
elsif infinite?(maximum)
-
"expected at least #{times(required)}"
-
elsif required == 0
-
"expected at most #{times(maximum)}"
-
else
-
"expected between #{required} and #{times(maximum)}"
-
end
-
end
-
end
-
-
1
protected
-
-
1
attr_reader :required, :maximum
-
-
1
def times(number)
-
case number
-
when 0 then "no times"
-
when 1 then "once"
-
when 2 then "twice"
-
else "#{number} times"
-
end
-
end
-
-
1
def infinite?(number)
-
number.respond_to?(:infinite?) && number.infinite?
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Central
-
-
1
attr_accessor :stubba_methods
-
-
1
def initialize
-
610
self.stubba_methods = []
-
end
-
-
1
def stub(method)
-
89
unless stubba_methods.detect { |m| m.matches?(method) }
-
85
method.stub
-
85
stubba_methods.push(method)
-
end
-
end
-
-
1
def unstub(method)
-
170
if existing = stubba_methods.detect { |m| m.matches?(method) }
-
85
existing.unstub
-
85
stubba_methods.delete(existing)
-
end
-
end
-
-
1
def unstub_all
-
610
while stubba_methods.any? do
-
85
unstub(stubba_methods.first)
-
end
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ChangeStateSideEffect
-
-
1
def initialize(state)
-
@state = state
-
end
-
-
1
def perform
-
@state.activate
-
end
-
-
1
def mocha_inspect
-
"then #{@state.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'metaclass'
-
-
1
module Mocha
-
-
1
class ClassMethod
-
-
1
attr_reader :stubbee, :method
-
-
1
def initialize(stubbee, method)
-
87
@stubbee, @original_method = stubbee, nil
-
87
@method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
-
end
-
-
1
def stub
-
85
hide_original_method
-
85
define_new_method
-
end
-
-
1
def unstub
-
85
remove_new_method
-
85
restore_original_method
-
85
mock.unstub(method.to_sym)
-
85
unless mock.any_expectations?
-
85
reset_mocha
-
end
-
end
-
-
1
def mock
-
170
stubbee.mocha
-
end
-
-
1
def reset_mocha
-
85
stubbee.reset_mocha
-
end
-
-
1
def hide_original_method
-
85
if method_exists?(method)
-
84
begin
-
84
@original_method = stubbee._method(method)
-
84
if @original_method && @original_method.owner == stubbee.__metaclass__
-
2
@original_visibility = :public
-
2
if stubbee.__metaclass__.protected_instance_methods.include?(method)
-
@original_visibility = :protected
-
elsif stubbee.__metaclass__.private_instance_methods.include?(method)
-
@original_visibility = :private
-
end
-
2
stubbee.__metaclass__.send(:remove_method, method)
-
end
-
rescue NameError
-
# deal with nasties like ActiveRecord::Associations::AssociationProxy
-
end
-
end
-
end
-
-
1
def define_new_method
-
85
stubbee.__metaclass__.class_eval(%{
-
def #{method}(*args, &block)
-
mocha.method_missing(:#{method}, *args, &block)
-
end
-
}, __FILE__, __LINE__)
-
end
-
-
1
def remove_new_method
-
85
stubbee.__metaclass__.send(:remove_method, method)
-
end
-
-
1
def restore_original_method
-
85
if @original_method && @original_method.owner == stubbee.__metaclass__
-
2
if RUBY_VERSION < '1.9'
-
original_method = @original_method
-
stubbee.__metaclass__.send(:define_method, method) do |*args, &block|
-
original_method.call(*args, &block)
-
end
-
else
-
2
stubbee.__metaclass__.send(:define_method, method, @original_method)
-
end
-
2
stubbee.__metaclass__.send(@original_visibility, method)
-
end
-
end
-
-
1
def matches?(other)
-
87
return false unless (other.class == self.class)
-
87
(stubbee.object_id == other.stubbee.object_id) and (method == other.method)
-
end
-
-
1
alias_method :==, :eql?
-
-
1
def to_s
-
"#{stubbee}.#{method}"
-
end
-
-
1
def method_exists?(method)
-
4
symbol = method.to_sym
-
4
__metaclass__ = stubbee.__metaclass__
-
4
__metaclass__.public_method_defined?(symbol) || __metaclass__.protected_method_defined?(symbol) || __metaclass__.private_method_defined?(symbol)
-
end
-
-
end
-
-
end
-
1
require 'mocha/mockery'
-
1
require 'mocha/class_method'
-
1
require 'mocha/any_instance_method'
-
-
1
module Mocha
-
-
# Methods added to all classes to allow mocking and stubbing on real (i.e. non-mock) objects.
-
1
module ClassMethods
-
-
# @private
-
1
def stubba_method
-
4
Mocha::ClassMethod
-
end
-
-
# @private
-
1
class AnyInstance
-
-
1
def initialize(klass)
-
@stubba_object = klass
-
end
-
-
1
def mocha
-
@mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object)
-
end
-
-
1
def stubba_method
-
Mocha::AnyInstanceMethod
-
end
-
-
1
def stubba_object
-
@stubba_object
-
end
-
-
1
def method_exists?(method, include_public_methods = true)
-
if include_public_methods
-
return true if @stubba_object.public_instance_methods(include_superclass_methods = true).include?(method)
-
end
-
return true if @stubba_object.protected_instance_methods(include_superclass_methods = true).include?(method)
-
return true if @stubba_object.private_instance_methods(include_superclass_methods = true).include?(method)
-
return false
-
end
-
-
end
-
-
# @return [Mock] a mock object which will detect calls to any instance of this class.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Return false to invocation of +Product#save+ for any instance of +Product+.
-
# Product.any_instance.stubs(:save).returns(false)
-
# product_1 = Product.new
-
# assert_equal false, product_1.save
-
# product_2 = Product.new
-
# assert_equal false, product_2.save
-
1
def any_instance
-
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}.any_instance", caller)
-
end
-
@any_instance ||= AnyInstance.new(self)
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Configuration settings.
-
1
class Configuration
-
-
1
DEFAULTS = {
-
:stubbing_method_unnecessarily => :allow,
-
:stubbing_method_on_non_mock_object => :allow,
-
:stubbing_non_existent_method => :allow,
-
:stubbing_non_public_method => :allow,
-
:stubbing_method_on_nil => :prevent,
-
}
-
-
1
class << self
-
-
# Allow the specified +action+.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def allow(action, &block)
-
change_config action, :allow, &block
-
end
-
-
# @private
-
1
def allow?(action)
-
448
configuration[action] == :allow
-
end
-
-
# Warn if the specified +action+ is attempted.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def warn_when(action, &block)
-
change_config action, :warn, &block
-
end
-
-
# @private
-
1
def warn_when?(action)
-
configuration[action] == :warn
-
end
-
-
# Raise a {StubbingError} if if the specified +action+ is attempted.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def prevent(action, &block)
-
change_config action, :prevent, &block
-
end
-
-
# @private
-
1
def prevent?(action)
-
configuration[action] == :prevent
-
end
-
-
# @private
-
1
def reset_configuration
-
@configuration = nil
-
end
-
-
1
private
-
-
# @private
-
1
def configuration
-
448
@configuration ||= DEFAULTS.dup
-
end
-
-
# @private
-
1
def change_config(action, new_value, &block)
-
if block_given?
-
temporarily_change_config action, new_value, &block
-
else
-
configuration[action] = new_value
-
end
-
end
-
-
# @private
-
1
def temporarily_change_config(action, new_value, &block)
-
original_value = configuration[action]
-
configuration[action] = new_value
-
yield
-
ensure
-
configuration[action] = original_value
-
end
-
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ExceptionRaiser
-
-
1
def initialize(exception, message)
-
@exception, @message = exception, message
-
end
-
-
1
def evaluate
-
raise @exception, @exception.to_s if @exception.is_a?(Module) && (@exception < Interrupt)
-
raise @exception, @message if @message
-
raise @exception
-
end
-
-
end
-
-
end
-
1
require 'mocha/method_matcher'
-
1
require 'mocha/parameters_matcher'
-
1
require 'mocha/expectation_error'
-
1
require 'mocha/return_values'
-
1
require 'mocha/exception_raiser'
-
1
require 'mocha/thrower'
-
1
require 'mocha/yield_parameters'
-
1
require 'mocha/is_a'
-
1
require 'mocha/in_state_ordering_constraint'
-
1
require 'mocha/change_state_side_effect'
-
1
require 'mocha/cardinality'
-
-
1
module Mocha
-
-
# Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}.
-
1
class Expectation
-
-
# Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
-
#
-
# @param [Range,Integer] range specifies the allowable range in the number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Specifying a specific number of expected invocations.
-
# object = mock()
-
# object.expects(:expected_method).times(3)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).times(3)
-
# 2.times { object.expected_method }
-
# # => verify fails
-
#
-
# @example Specifying a range in the number of expected invocations.
-
# object = mock()
-
# object.expects(:expected_method).times(2..4)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).times(2..4)
-
# object.expected_method
-
# # => verify fails
-
1
def times(range)
-
@cardinality = Cardinality.times(range)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called exactly twice.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be invoked exactly twice.
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# object.expected_method
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# # => verify fails
-
1
def twice
-
@cardinality = Cardinality.exactly(2)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called exactly once.
-
#
-
# Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be invoked exactly once.
-
# object = mock()
-
# object.expects(:expected_method).once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).once
-
# object.expected_method
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).once
-
# # => verify fails
-
1
def once
-
1
@cardinality = Cardinality.exactly(1)
-
1
self
-
end
-
-
# Modifies expectation so that the expected method must never be called.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must never be called.
-
# object = mock()
-
# object.expects(:expected_method).never
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).never
-
# # => verify succeeds
-
1
def never
-
1
@cardinality = Cardinality.exactly(0)
-
1
self
-
end
-
-
# Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
-
#
-
# @param [Integer] minimum_number_of_times minimum number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at least twice.
-
# object = mock()
-
# object.expects(:expected_method).at_least(2)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_least(2)
-
# object.expected_method
-
# # => verify fails
-
1
def at_least(minimum_number_of_times)
-
6
@cardinality = Cardinality.at_least(minimum_number_of_times)
-
6
self
-
end
-
-
# Modifies expectation so that the expected method must be called at least once.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at least once.
-
# object = mock()
-
# object.expects(:expected_method).at_least_once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_least_once
-
# # => verify fails
-
1
def at_least_once
-
at_least(1)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
-
#
-
# @param [Integer] maximum_number_of_times maximum number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at most twice.
-
# object = mock()
-
# object.expects(:expected_method).at_most(2)
-
# 2.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_most(2)
-
# 3.times { object.expected_method } # => unexpected invocation
-
1
def at_most(maximum_number_of_times)
-
@cardinality = Cardinality.at_most(maximum_number_of_times)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called at most once.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at most once.
-
# object = mock()
-
# object.expects(:expected_method).at_most_once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_most_once
-
# 2.times { object.expected_method } # => unexpected invocation
-
1
def at_most_once()
-
at_most(1)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called with +expected_parameters+.
-
#
-
# May be used with parameter matchers in {ParameterMatchers}.
-
#
-
# @param [*Array] expected_parameters parameters expected.
-
# @yield optional block specifying custom matching.
-
# @yieldparam [*Array] actual_parameters parameters with which expected method was invoked.
-
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called with expected parameters.
-
# object = mock()
-
# object.expects(:expected_method).with(:param1, :param2)
-
# object.expected_method(:param1, :param2)
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).with(:param1, :param2)
-
# object.expected_method(:param3)
-
# # => verify fails
-
#
-
# @example Expected method must be called with a value divisible by 4.
-
# object = mock()
-
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
-
# object.expected_method(16)
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
-
# object.expected_method(17)
-
# # => verify fails
-
1
def with(*expected_parameters, &matching_block)
-
93
@parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
-
93
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it yields with the specified +parameters+.
-
#
-
# May be called multiple times on the same expectation for consecutive invocations.
-
#
-
# @param [*Array] parameters parameters to be yielded.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @example Yield parameters when expected method is invoked.
-
# object = mock()
-
# object.expects(:expected_method).yields('result')
-
# yielded_value = nil
-
# object.expected_method { |value| yielded_value = value }
-
# yielded_value # => 'result'
-
#
-
# @example Yield different parameters on different invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).yields(1).then.yields(2)
-
# yielded_values_from_first_invocation = []
-
# yielded_values_from_second_invocation = []
-
# object.expected_method { |value| yielded_values_from_first_invocation << value } # first invocation
-
# object.expected_method { |value| yielded_values_from_second_invocation << value } # second invocation
-
# yielded_values_from_first_invocation # => [1]
-
# yielded_values_from_second_invocation # => [2]
-
1
def yields(*parameters)
-
@yield_parameters.add(*parameters)
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+.
-
#
-
# @param [*Array<Array>] parameter_groups each element of +parameter_groups+ should iself be an +Array+ representing the parameters to be passed to the block for a single yield.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @example When the +expected_method+ is called, the stub will invoke the block twice, the first time it passes +'result_1'+, +'result_2'+ as the parameters, and the second time it passes 'result_3' as the parameters.
-
# object = mock()
-
# object.expects(:expected_method).multiple_yields(['result_1', 'result_2'], ['result_3'])
-
# yielded_values = []
-
# object.expected_method { |*values| yielded_values << values }
-
# yielded_values # => [['result_1', 'result_2'], ['result_3]]
-
#
-
# @example Yield different groups of parameters on different invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).multiple_yields([1, 2], [3]).then.multiple_yields([4], [5, 6])
-
# yielded_values_from_first_invocation = []
-
# yielded_values_from_second_invocation = []
-
# object.expected_method { |*values| yielded_values_from_first_invocation << values } # first invocation
-
# object.expected_method { |*values| yielded_values_from_second_invocation << values } # second invocation
-
# yielded_values_from_first_invocation # => [[1, 2], [3]]
-
# yielded_values_from_second_invocation # => [[4], [5, 6]]
-
1
def multiple_yields(*parameter_groups)
-
@yield_parameters.multiple_add(*parameter_groups)
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it returns the specified +value+.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @overload def returns(value)
-
# @param [Object] value value to return on invocation of expected method.
-
# @overload def returns(*values)
-
# @param [*Array] values values to return on consecutive invocations of expected method.
-
#
-
# @example Return the same value on every invocation.
-
# object = mock()
-
# object.stubs(:stubbed_method).returns('result')
-
# object.stubbed_method # => 'result'
-
# object.stubbed_method # => 'result'
-
#
-
# @example Return a different value on consecutive invocations.
-
# object = mock()
-
# object.stubs(:stubbed_method).returns(1, 2)
-
# object.stubbed_method # => 1
-
# object.stubbed_method # => 2
-
#
-
# @example Alternative way to return a different value on consecutive invocations.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.returns(3)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
#
-
# @example May be called in conjunction with {#raises} on the same expectation.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => raises exception of class Exception1
-
#
-
# @example Note that in Ruby a method returning multiple values is exactly equivalent to a method returning an +Array+ of those values.
-
# object = mock()
-
# object.stubs(:expected_method).returns([1, 2])
-
# x, y = object.expected_method
-
# x # => 1
-
# y # => 2
-
1
def returns(*values)
-
6
@return_values += ReturnValues.build(*values)
-
6
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+ i.e. calls +Kernel#raise(exception, message)+.
-
#
-
# @param [Class,Exception,String,#exception] exception exception to be raised or message to be passed to RuntimeError.
-
# @param [String] message exception message.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see Kernel#raise
-
# @see #then
-
#
-
# @overload def raises
-
# @overload def raises(exception)
-
# @overload def raises(exception, message)
-
#
-
# @example Raise specified exception if expected method is invoked.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception, 'message')
-
# object.expected_method # => raises exception of class Exception and with message 'message'
-
#
-
# @example Raise custom exception with extra constructor parameters by passing in an instance of the exception.
-
# object = stub()
-
# object.stubs(:expected_method).raises(MyException.new('message', 1, 2, 3))
-
# object.expected_method # => raises the specified instance of MyException
-
#
-
# @example Raise different exceptions on consecutive invocations of the expected method.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception1).then.raises(Exception2)
-
# object.expected_method # => raises exception of class Exception1
-
# object.expected_method # => raises exception of class Exception2
-
#
-
# @example Raise an exception on first invocation of expected method and then return values on subsequent invocations.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception).then.returns(2, 3)
-
# object.expected_method # => raises exception of class Exception1
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
1
def raises(exception = RuntimeError, message = nil)
-
@return_values += ReturnValues.new(ExceptionRaiser.new(exception, message))
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it throws the specified +tag+ with the specific return value +object+ i.e. calls +Kernel#throw(tag, object)+.
-
#
-
# @param [Symbol,String] tag tag to throw to transfer control to the active catch block.
-
# @param [Object] object return value for the catch block.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see Kernel#throw
-
# @see #then
-
#
-
# @overload def throw(tag)
-
# @overload def throw(tag, object)
-
#
-
# @example Throw tag when expected method is invoked.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done)
-
# object.expected_method # => throws tag :done
-
#
-
# @example Throw tag with return value +object+ c.f. +Kernel#throw+.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done, 'result')
-
# object.expected_method # => throws tag :done and causes catch block to return 'result'
-
#
-
# @example Throw different tags on consecutive invocations of the expected method.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done).then.throws(:continue)
-
# object.expected_method # => throws :done
-
# object.expected_method # => throws :continue
-
#
-
# @example Throw tag on first invocation of expected method and then return values for subsequent invocations.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done).then.returns(2, 3)
-
# object.expected_method # => throws :done
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
1
def throws(tag, object = nil)
-
@return_values += ReturnValues.new(Thrower.new(tag, object))
-
self
-
end
-
-
# @overload def then
-
# Used as syntactic sugar to improve readability. It has no effect on state of the expectation.
-
# @overload def then(state_machine.is(state_name))
-
# Used to change the +state_machine+ to the state specified by +state_name+ when the expected invocation occurs.
-
# @param [StateMachine::State] state_machine.is(state_name) provides a mechanism to change the +state_machine+ into the state specified by +state_name+ when the expected method is invoked.
-
#
-
# @see API#states
-
# @see StateMachine
-
# @see #when
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Using {#then} as syntactic sugar when specifying values to be returned and exceptions to be raised on consecutive invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => raises exception of class Exception
-
# object.expected_method # => 4
-
#
-
# @example Using {#then} to change the +state+ of a +state_machine+ on the invocation of an expected method.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio')
-
# radio.expects(:switch_on).then(power.is('on'))
-
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
-
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
-
# radio.expects(:switch_off).then(power.is('off'))
-
1
def then(*parameters)
-
if parameters.length == 1
-
state = parameters.first
-
add_side_effect(ChangeStateSideEffect.new(state))
-
end
-
self
-
end
-
-
# Constrains the expectation to occur only when the +state_machine+ is in the state specified by +state_name+.
-
#
-
# @param [StateMachine::StatePredicate] state_machine.is(state_name) provides a mechanism to determine whether the +state_machine+ is in the state specified by +state_name+ when the expected method is invoked.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see API#states
-
# @see StateMachine
-
# @see #then
-
#
-
# @example Using {#when} to only allow invocation of methods when "power" state machine is in the "on" state.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio')
-
# radio.expects(:switch_on).then(power.is('on'))
-
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
-
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
-
# radio.expects(:switch_off).then(power.is('off'))
-
1
def when(state_predicate)
-
add_ordering_constraint(InStateOrderingConstraint.new(state_predicate))
-
self
-
end
-
-
# Constrains the expectation so that it must be invoked at the current point in the +sequence+.
-
#
-
# To expect a sequence of invocations, write the expectations in order and add the +in_sequence(sequence)+ clause to each one.
-
#
-
# Expectations in a +sequence+ can have any invocation count.
-
#
-
# If an expectation in a sequence is stubbed, rather than expected, it can be skipped in the +sequence+.
-
#
-
# An expected method can appear in multiple sequences.
-
#
-
# @param [*Array<Sequence>] sequences sequences in which expected method should appear.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see API#sequence
-
#
-
# @example Ensure methods are invoked in a specified order.
-
# breakfast = sequence('breakfast')
-
#
-
# egg = mock('egg')
-
# egg.expects(:crack).in_sequence(breakfast)
-
# egg.expects(:fry).in_sequence(breakfast)
-
# egg.expects(:eat).in_sequence(breakfast)
-
1
def in_sequence(*sequences)
-
sequences.each { |sequence| add_in_sequence_ordering_constraint(sequence) }
-
self
-
end
-
-
# @private
-
1
attr_reader :backtrace
-
-
# @private
-
1
def initialize(mock, expected_method_name, backtrace = nil)
-
100
@mock = mock
-
100
@method_matcher = MethodMatcher.new(expected_method_name.to_sym)
-
100
@parameters_matcher = ParametersMatcher.new
-
100
@ordering_constraints = []
-
100
@side_effects = []
-
100
@cardinality, @invocation_count = Cardinality.exactly(1), 0
-
100
@return_values = ReturnValues.new
-
100
@yield_parameters = YieldParameters.new
-
100
@backtrace = backtrace || caller
-
end
-
-
# @private
-
1
def add_ordering_constraint(ordering_constraint)
-
@ordering_constraints << ordering_constraint
-
end
-
-
# @private
-
1
def add_in_sequence_ordering_constraint(sequence)
-
sequence.constrain_as_next_in_sequence(self)
-
end
-
-
# @private
-
1
def add_side_effect(side_effect)
-
@side_effects << side_effect
-
end
-
-
# @private
-
1
def perform_side_effects
-
99
@side_effects.each { |side_effect| side_effect.perform }
-
end
-
-
# @private
-
1
def in_correct_order?
-
99
@ordering_constraints.all? { |ordering_constraint| ordering_constraint.allows_invocation_now? }
-
end
-
-
# @private
-
1
def matches_method?(method_name)
-
97
@method_matcher.match?(method_name)
-
end
-
-
# @private
-
1
def match?(actual_method_name, *actual_parameters)
-
190
@method_matcher.match?(actual_method_name) && @parameters_matcher.match?(actual_parameters) && in_correct_order?
-
end
-
-
# @private
-
1
def invocations_allowed?
-
99
@cardinality.invocations_allowed?(@invocation_count)
-
end
-
-
# @private
-
1
def satisfied?
-
@cardinality.satisfied?(@invocation_count)
-
end
-
-
# @private
-
1
def invoke
-
99
@invocation_count += 1
-
99
perform_side_effects()
-
99
if block_given? then
-
@yield_parameters.next_invocation.each do |yield_parameters|
-
yield(*yield_parameters)
-
end
-
end
-
99
@return_values.next
-
end
-
-
# @private
-
1
def verified?(assertion_counter = nil)
-
100
assertion_counter.increment if assertion_counter && @cardinality.needs_verifying?
-
100
@cardinality.verified?(@invocation_count)
-
end
-
-
# @private
-
1
def used?
-
@cardinality.used?(@invocation_count)
-
end
-
-
# @private
-
1
def mocha_inspect
-
message = "#{@cardinality.mocha_inspect}, "
-
message << case @invocation_count
-
when 0 then "not yet invoked"
-
when 1 then "invoked once"
-
when 2 then "invoked twice"
-
else "invoked #{@invocation_count} times"
-
end
-
message << ": "
-
message << method_signature
-
message << "; #{@ordering_constraints.map { |oc| oc.mocha_inspect }.join("; ")}" unless @ordering_constraints.empty?
-
message
-
end
-
-
# @private
-
1
def method_signature
-
"#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
# Default exception class raised when an unexpected invocation or an unsatisfied expectation occurs.
-
#
-
# Authors of test libraries may use +Mocha::ExpectationErrorFactory+ to have Mocha raise a different exception.
-
#
-
# @see Mocha::ExpectationErrorFactory
-
1
class ExpectationError < Exception; end
-
end
-
1
require 'mocha/backtrace_filter'
-
1
require 'mocha/expectation_error'
-
-
1
module Mocha
-
-
# This factory determines what class of exception should be raised when Mocha detects a test failure.
-
#
-
# This class should only be used by authors of test libraries and not by typical "users" of Mocha.
-
#
-
# For example, it is used by +Mocha::Integration::MiniTest::Adapter+ in order to have Mocha raise a +MiniTest::Assertion+ which can then be sensibly handled by +MiniTest::Unit::TestCase+.
-
#
-
# @see Mocha::Integration::MiniTest::Adapter
-
1
class ExpectationErrorFactory
-
1
class << self
-
# @!attribute exception_class
-
# Determines what class of exception should be raised when Mocha detects a test failure.
-
#
-
# This attribute may be set by authors of test libraries in order to have Mocha raise exceptions of a specific class when there is an unexpected invocation or an unsatisfied expectation.
-
#
-
# By default a +Mocha::ExpectationError+ will be raised.
-
#
-
# @return [Exception] class of exception to be raised when an expectation error occurs
-
# @see Mocha::ExpectationError
-
1
attr_accessor :exception_class
-
-
# @private
-
1
def build(message = nil, backtrace = [])
-
self.exception_class ||= ExpectationError
-
exception = exception_class.new(message)
-
filter = BacktraceFilter.new
-
exception.set_backtrace(filter.filtered(backtrace))
-
exception
-
end
-
end
-
end
-
end
-
1
module Mocha
-
-
1
class ExpectationList
-
-
1
def initialize
-
91
@expectations = []
-
end
-
-
1
def add(expectation)
-
100
@expectations.unshift(expectation)
-
100
expectation
-
end
-
-
1
def remove_all_matching_method(method_name)
-
172
@expectations.reject! { |expectation| expectation.matches_method?(method_name) }
-
end
-
-
1
def matches_method?(method_name)
-
15
@expectations.any? { |expectation| expectation.matches_method?(method_name) }
-
end
-
-
1
def match(method_name, *arguments)
-
8
matching_expectations(method_name, *arguments).first
-
end
-
-
1
def match_allowing_invocation(method_name, *arguments)
-
206
matching_expectations(method_name, *arguments).detect { |e| e.invocations_allowed? }
-
end
-
-
1
def verified?(assertion_counter = nil)
-
191
@expectations.all? { |expectation| expectation.verified?(assertion_counter) }
-
end
-
-
1
def to_a
-
91
@expectations
-
end
-
-
1
def to_set
-
@expectations.to_set
-
end
-
-
1
def length
-
@expectations.length
-
end
-
-
1
def any?
-
85
@expectations.any?
-
end
-
-
1
private
-
-
1
def matching_expectations(method_name, *arguments)
-
305
@expectations.select { |e| e.match?(method_name, *arguments) }
-
end
-
-
end
-
-
end
-
1
require 'mocha/mockery'
-
-
1
module Mocha
-
-
# Integration hooks for test library authors.
-
#
-
# The methods in this module should be called from test libraries wishing to integrate with Mocha.
-
#
-
# This module is provided as part of the +Mocha::API+ module and is therefore part of the public API, but should only be used by authors of test libraries and not by typical "users" of Mocha.
-
#
-
# Integration with Test::Unit and MiniTest are provided as part of Mocha, because they are (or were once) part of the Ruby standard library. Integration with other test libraries is not provided as *part* of Mocha, but is supported by means of the methods in this module.
-
#
-
# See the code in the +Adapter+ modules for examples of how to use the methods in this module. +Mocha::ExpectationErrorFactory+ may be used if you want +Mocha+ to raise a different type of exception.
-
#
-
# @see Mocha::Integration::TestUnit::Adapter
-
# @see Mocha::Integration::MiniTest::Adapter
-
# @see Mocha::ExpectationErrorFactory
-
# @see Mocha::API
-
1
module Hooks
-
# Prepares Mocha before a test (only for use by authors of test libraries).
-
#
-
# This method should be called before each individual test starts (including before any "setup" code).
-
1
def mocha_setup
-
end
-
-
# Verifies that all mock expectations have been met (only for use by authors of test libraries).
-
#
-
# This is equivalent to a series of "assertions".
-
#
-
# This method should be called at the end of each individual test, before it has been determined whether or not the test has passed.
-
1
def mocha_verify(assertion_counter = nil)
-
610
Mockery.instance.verify(assertion_counter)
-
end
-
-
# Resets Mocha after a test (only for use by authors of test libraries).
-
#
-
# This method should be called after each individual test has finished (including after any "teardown" code).
-
1
def mocha_teardown
-
610
Mockery.instance.teardown
-
610
Mockery.reset_instance
-
end
-
end
-
end
-
1
module Mocha
-
-
1
class InStateOrderingConstraint
-
-
1
def initialize(state_predicate)
-
@state_predicate = state_predicate
-
end
-
-
1
def allows_invocation_now?
-
@state_predicate.active?
-
end
-
-
1
def mocha_inspect
-
"when #{@state_predicate.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'date'
-
-
1
module Mocha
-
-
1
module ObjectMethods
-
1
def mocha_inspect
-
address = self.__id__ * 2
-
address += 0x100000000 if address < 0
-
inspect =~ /#</ ? "#<#{self.class}:0x#{'%x' % address}>" : inspect
-
end
-
end
-
-
1
module StringMethods
-
1
def mocha_inspect
-
inspect.gsub(/\"/, "'")
-
end
-
end
-
-
1
module ArrayMethods
-
1
def mocha_inspect
-
"[#{collect { |member| member.mocha_inspect }.join(', ')}]"
-
end
-
end
-
-
1
module HashMethods
-
1
def mocha_inspect
-
"{#{collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')}}"
-
end
-
end
-
-
1
module TimeMethods
-
1
def mocha_inspect
-
"#{inspect} (#{to_f} secs)"
-
end
-
end
-
-
1
module DateMethods
-
1
def mocha_inspect
-
to_s
-
end
-
end
-
-
end
-
-
1
class Object
-
1
include Mocha::ObjectMethods
-
end
-
-
1
class String
-
1
include Mocha::StringMethods
-
end
-
-
1
class Array
-
1
include Mocha::ArrayMethods
-
end
-
-
1
class Hash
-
1
include Mocha::HashMethods
-
end
-
-
1
class Time
-
1
include Mocha::TimeMethods
-
end
-
-
1
class Date
-
1
include Mocha::DateMethods
-
end
-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class InstanceMethod < ClassMethod
-
-
1
def method_exists?(method)
-
81
return true if stubbee.public_methods(false).include?(method)
-
return true if stubbee.protected_methods(false).include?(method)
-
return true if stubbee.private_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
class Object
-
-
# :stopdoc:
-
-
1
alias_method :__is_a__, :is_a?
-
-
# :startdoc:
-
-
end
-
1
module Mocha
-
-
1
class Logger
-
-
1
def initialize(io)
-
@io = io
-
end
-
-
1
def warn(message)
-
@io.puts "WARNING: #{message}"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class MethodMatcher
-
-
1
attr_reader :expected_method_name
-
-
1
def initialize(expected_method_name)
-
100
@expected_method_name = expected_method_name
-
end
-
-
1
def match?(actual_method_name)
-
287
@expected_method_name == actual_method_name.to_sym
-
end
-
-
1
def mocha_inspect
-
"#{@expected_method_name}"
-
end
-
-
end
-
-
end
-
1
require 'metaclass'
-
1
require 'mocha/expectation'
-
1
require 'mocha/expectation_list'
-
1
require 'mocha/names'
-
1
require 'mocha/method_matcher'
-
1
require 'mocha/parameters_matcher'
-
1
require 'mocha/unexpected_invocation'
-
1
require 'mocha/argument_iterator'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
# Traditional mock object.
-
#
-
# All methods return an {Expectation} which can be further modified by methods on {Expectation}.
-
1
class Mock
-
-
# Adds an expectation that the specified method must be called exactly once with any parameters.
-
#
-
# @param [Symbol,String] method_name name of expected method
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
-
#
-
# @overload def expects(method_name)
-
# @overload def expects(expected_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
#
-
# @example Expected method invoked once so no error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# object.expected_method
-
#
-
# @example Expected method not invoked so error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# # error raised when test completes, because expected_method not called exactly once
-
#
-
# @example Expected method invoked twice so error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# object.expected_method
-
# object.expected_method # => error raised when expected method invoked second time
-
#
-
# @example Setup multiple expectations using +expected_methods_vs_return_values+.
-
# object = mock()
-
# object.expects(:expected_method_one => :result_one, :expected_method_two => :result_two)
-
#
-
# # is exactly equivalent to
-
#
-
# object = mock()
-
# object.expects(:expected_method_one).returns(:result_one)
-
# object.expects(:expected_method_two).returns(:result_two)
-
1
def expects(method_name_or_hash, backtrace = nil)
-
95
iterator = ArgumentIterator.new(method_name_or_hash)
-
95
iterator.each { |*args|
-
94
method_name = args.shift
-
94
ensure_method_not_already_defined(method_name)
-
94
expectation = Expectation.new(self, method_name, backtrace)
-
94
expectation.returns(args.shift) if args.length > 0
-
94
@expectations.add(expectation)
-
}
-
end
-
-
# Adds an expectation that the specified method may be called any number of times with any parameters.
-
#
-
# @param [Symbol,String] method_name name of stubbed method
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
-
#
-
# @overload def stubs(method_name)
-
# @overload def stubs(stubbed_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
#
-
# @example No error raised however many times stubbed method is invoked
-
# object = mock()
-
# object.stubs(:stubbed_method)
-
# object.stubbed_method
-
# object.stubbed_method
-
# # no error raised
-
#
-
# @example Setup multiple expectations using +stubbed_methods_vs_return_values+.
-
# object = mock()
-
# object.stubs(:stubbed_method_one => :result_one, :stubbed_method_two => :result_two)
-
#
-
# # is exactly equivalent to
-
#
-
# object = mock()
-
# object.stubs(:stubbed_method_one).returns(:result_one)
-
# object.stubs(:stubbed_method_two).returns(:result_two)
-
1
def stubs(method_name_or_hash, backtrace = nil)
-
11
iterator = ArgumentIterator.new(method_name_or_hash)
-
11
iterator.each { |*args|
-
6
method_name = args.shift
-
6
ensure_method_not_already_defined(method_name)
-
6
expectation = Expectation.new(self, method_name, backtrace)
-
6
expectation.at_least(0)
-
6
expectation.returns(args.shift) if args.length > 0
-
6
@expectations.add(expectation)
-
}
-
end
-
-
# Removes the specified stubbed method (added by calls to {#expects} or {#stubs}) and all expectations associated with it.
-
#
-
# @param [Symbol] method_name name of method to unstub.
-
#
-
# @example Invoking an unstubbed method causes error to be raised
-
# object = mock('mock') do
-
# object.stubs(:stubbed_method).returns(:result_one)
-
# object.stubbed_method # => :result_one
-
# object.unstub(:stubbed_method)
-
# object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
-
1
def unstub(method_name)
-
85
@expectations.remove_all_matching_method(method_name)
-
end
-
-
# Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
-
#
-
# A +NoMethodError+ will be raised if the +responder+ does not +#respond_to?+ a method invocation (even if the method has been expected or stubbed).
-
#
-
# The {Mock} instance will delegate its +#respond_to?+ method to the +responder+.
-
#
-
# @param [Object, #respond_to?] responder an object used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
-
# @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.
-
#
-
# @example Normal mocking
-
# sheep = mock('sheep')
-
# sheep.expects(:chew)
-
# sheep.expects(:foo)
-
# sheep.respond_to?(:chew) # => true
-
# sheep.respond_to?(:foo) # => true
-
# sheep.chew
-
# sheep.foo
-
# # no error raised
-
#
-
# @example Using {#responds_like} with an instance method
-
# class Sheep
-
# def chew(grass); end
-
# end
-
#
-
# sheep = mock('sheep')
-
# sheep.responds_like(Sheep.new)
-
# sheep.expects(:chew)
-
# sheep.expects(:foo)
-
# sheep.respond_to?(:chew) # => true
-
# sheep.respond_to?(:foo) # => false
-
# sheep.chew
-
# sheep.foo # => raises NoMethodError exception
-
#
-
# @example Using {#responds_like} with a class method
-
# class Sheep
-
# def self.number_of_legs; end
-
# end
-
#
-
# sheep_class = mock('sheep_class')
-
# sheep_class.responds_like(Sheep)
-
# sheep_class.stubs(:number_of_legs).returns(4)
-
# sheep_class.expects(:foo)
-
# sheep_class.respond_to?(:number_of_legs) # => true
-
# sheep_class.respond_to?(:foo) # => false
-
# assert_equal 4, sheep_class.number_of_legs
-
# sheep_class.foo # => raises NoMethodError exception
-
1
def responds_like(responder)
-
@responder = responder
-
self
-
end
-
-
# @private
-
1
def initialize(mockery, name = nil, &block)
-
91
@mockery = mockery
-
91
@name = name || DefaultName.new(self)
-
91
@expectations = ExpectationList.new
-
91
@everything_stubbed = false
-
91
@responder = nil
-
91
instance_eval(&block) if block
-
end
-
-
# @private
-
1
attr_reader :everything_stubbed
-
-
1
alias_method :__expects__, :expects
-
-
1
alias_method :__stubs__, :stubs
-
-
1
alias_method :quacks_like, :responds_like
-
-
# @private
-
1
def __expectations__
-
91
@expectations
-
end
-
-
# @private
-
1
def stub_everything
-
2
@everything_stubbed = true
-
end
-
-
# @private
-
1
def method_missing(symbol, *arguments, &block)
-
107
if @responder and not @responder.respond_to?(symbol)
-
raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
-
end
-
107
if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
-
99
matching_expectation_allowing_invocation.invoke(&block)
-
else
-
8
if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
-
matching_expectation.invoke(&block) if matching_expectation
-
message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
-
message << @mockery.mocha_inspect
-
raise ExpectationErrorFactory.build(message, caller)
-
end
-
end
-
end
-
-
# @private
-
1
def respond_to?(symbol, include_private = false)
-
5
if @responder then
-
if @responder.method(:respond_to?).arity > 1
-
@responder.respond_to?(symbol, include_private)
-
else
-
@responder.respond_to?(symbol)
-
end
-
else
-
5
@everything_stubbed || @expectations.matches_method?(symbol)
-
end
-
end
-
-
# @private
-
1
def __verified__?(assertion_counter = nil)
-
91
@expectations.verified?(assertion_counter)
-
end
-
-
# @private
-
1
def mocha_inspect
-
3
@name.mocha_inspect
-
end
-
-
# @private
-
1
def inspect
-
3
mocha_inspect
-
end
-
-
# @private
-
1
def ensure_method_not_already_defined(method_name)
-
100
self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
-
end
-
-
# @private
-
1
def any_expectations?
-
85
@expectations.any?
-
end
-
-
end
-
-
end
-
1
require 'mocha/central'
-
1
require 'mocha/mock'
-
1
require 'mocha/names'
-
1
require 'mocha/state_machine'
-
1
require 'mocha/logger'
-
1
require 'mocha/configuration'
-
1
require 'mocha/stubbing_error'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
1
class Mockery
-
-
1
class << self
-
-
1
def instance
-
1398
@instance ||= new
-
end
-
-
1
def reset_instance
-
610
@instance = nil
-
end
-
-
end
-
-
1
def named_mock(name, &block)
-
add_mock(Mock.new(self, Name.new(name), &block))
-
end
-
-
1
def unnamed_mock(&block)
-
6
add_mock(Mock.new(self, &block))
-
end
-
-
1
def mock_impersonating(object, &block)
-
85
add_mock(Mock.new(self, ImpersonatingName.new(object), &block))
-
end
-
-
1
def mock_impersonating_any_instance_of(klass, &block)
-
add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), &block))
-
end
-
-
1
def new_state_machine(name)
-
add_state_machine(StateMachine.new(name))
-
end
-
-
1
def verify(assertion_counter = nil)
-
701
unless mocks.all? { |mock| mock.__verified__?(assertion_counter) }
-
message = "not all expectations were satisfied\n#{mocha_inspect}"
-
if unsatisfied_expectations.empty?
-
backtrace = caller
-
else
-
backtrace = unsatisfied_expectations[0].backtrace
-
end
-
raise ExpectationErrorFactory.build(message, backtrace)
-
end
-
610
expectations.each do |e|
-
100
unless Mocha::Configuration.allow?(:stubbing_method_unnecessarily)
-
unless e.used?
-
on_stubbing_method_unnecessarily(e)
-
end
-
end
-
end
-
end
-
-
1
def teardown
-
610
stubba.unstub_all
-
610
reset
-
end
-
-
1
def stubba
-
697
@stubba ||= Central.new
-
end
-
-
1
def mocks
-
1311
@mocks ||= []
-
end
-
-
1
def state_machines
-
@state_machines ||= []
-
end
-
-
1
def mocha_inspect
-
message = ""
-
message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless unsatisfied_expectations.empty?
-
message << "satisfied expectations:\n- #{satisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless satisfied_expectations.empty?
-
message << "states:\n- #{state_machines.map { |sm| sm.mocha_inspect }.join("\n- ")}" unless state_machines.empty?
-
message
-
end
-
-
1
def on_stubbing(object, method)
-
87
method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
-
87
unless Mocha::Configuration.allow?(:stubbing_non_existent_method)
-
unless object.method_exists?(method, include_public_methods = true)
-
on_stubbing_non_existent_method(object, method)
-
end
-
end
-
87
unless Mocha::Configuration.allow?(:stubbing_non_public_method)
-
if object.method_exists?(method, include_public_methods = false)
-
on_stubbing_non_public_method(object, method)
-
end
-
end
-
87
unless Mocha::Configuration.allow?(:stubbing_method_on_nil)
-
87
if object.nil?
-
on_stubbing_method_on_nil(object, method)
-
end
-
end
-
87
unless Mocha::Configuration.allow?(:stubbing_method_on_non_mock_object)
-
on_stubbing_method_on_non_mock_object(object, method)
-
end
-
end
-
-
1
def on_stubbing_non_existent_method(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_non_existent_method)
-
raise StubbingError.new("stubbing non-existent method: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_non_existent_method)
-
logger.warn "stubbing non-existent method: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_non_public_method(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_non_public_method)
-
raise StubbingError.new("stubbing non-public method: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_non_public_method)
-
logger.warn "stubbing non-public method: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_on_nil(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_method_on_nil)
-
raise StubbingError.new("stubbing method on nil: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_on_nil)
-
logger.warn "stubbing method on nil: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_on_non_mock_object(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_method_on_non_mock_object)
-
raise StubbingError.new("stubbing method on non-mock object: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_on_non_mock_object)
-
logger.warn "stubbing method on non-mock object: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_unnecessarily(expectation)
-
if Mocha::Configuration.prevent?(:stubbing_method_unnecessarily)
-
raise StubbingError.new("stubbing method unnecessarily: #{expectation.method_signature}", expectation.backtrace)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_unnecessarily)
-
logger.warn "stubbing method unnecessarily: #{expectation.method_signature}"
-
end
-
end
-
-
1
attr_writer :logger
-
-
1
def logger
-
@logger ||= Logger.new($stderr)
-
end
-
-
-
1
private
-
-
1
def expectations
-
701
mocks.map { |mock| mock.__expectations__.to_a }.flatten
-
end
-
-
1
def unsatisfied_expectations
-
expectations.reject { |e| e.verified? }
-
end
-
-
1
def satisfied_expectations
-
expectations.select { |e| e.verified? }
-
end
-
-
1
def add_mock(mock)
-
91
mocks << mock
-
91
mock
-
end
-
-
1
def add_state_machine(state_machine)
-
state_machines << state_machine
-
state_machine
-
end
-
-
1
def reset
-
610
@stubba = nil
-
610
@mocks = nil
-
610
@state_machines = nil
-
end
-
-
end
-
-
end
-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class ModuleMethod < ClassMethod
-
-
1
def method_exists?(method)
-
return true if stubbee.public_methods(false).include?(method)
-
return true if stubbee.protected_methods(false).include?(method)
-
return true if stubbee.private_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
require 'mocha/module_method'
-
-
1
module Mocha
-
-
# @private
-
1
module ModuleMethods
-
-
1
def stubba_method
-
Mocha::ModuleMethod
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class MultipleYields
-
-
1
attr_reader :parameter_groups
-
-
1
def initialize(*parameter_groups)
-
@parameter_groups = parameter_groups
-
end
-
-
1
def each
-
@parameter_groups.each do |parameter_group|
-
yield(parameter_group)
-
end
-
end
-
-
end
-
-
end
-
-
1
module Mocha
-
-
1
class ImpersonatingName
-
-
1
def initialize(object)
-
85
@object = object
-
end
-
-
1
def mocha_inspect
-
@object.mocha_inspect
-
end
-
-
end
-
-
1
class ImpersonatingAnyInstanceName
-
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
1
def mocha_inspect
-
"#<AnyInstance:#{@klass.mocha_inspect}>"
-
end
-
-
end
-
-
1
class Name
-
-
1
def initialize(name)
-
@name = name
-
end
-
-
1
def mocha_inspect
-
"#<Mock:#{@name}>"
-
end
-
-
end
-
-
1
class DefaultName
-
-
1
def initialize(mock)
-
6
@mock = mock
-
end
-
-
1
def mocha_inspect
-
3
address = @mock.__id__ * 2
-
3
address += 0x100000000 if address < 0
-
3
"#<Mock:0x#{'%x' % address}>"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class NoYields
-
-
1
def each
-
end
-
-
end
-
-
end
-
-
1
require 'mocha/mockery'
-
1
require 'mocha/instance_method'
-
1
require 'mocha/argument_iterator'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
# Methods added to all objects to allow mocking and stubbing on real (i.e. non-mock) objects.
-
#
-
# Both {#expects} and {#stubs} return an {Expectation} which can be further modified by methods on {Expectation}.
-
1
module ObjectMethods
-
-
# @private
-
1
alias_method :_method, :method
-
-
# @private
-
1
def mocha
-
344
@mocha ||= Mocha::Mockery.instance.mock_impersonating(self)
-
end
-
-
# @private
-
1
def reset_mocha
-
85
@mocha = nil
-
end
-
-
# @private
-
1
def stubba_method
-
83
Mocha::InstanceMethod
-
end
-
-
# @private
-
1
def stubba_object
-
87
self
-
end
-
-
# Adds an expectation that the specified method must be called exactly once with any parameters.
-
#
-
# The original implementation of the method is replaced during the test and then restored at the end of the test.
-
#
-
# @param [Symbol,String] method_name name of expected method
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
-
#
-
# @overload def expects(method_name)
-
# @overload def expects(expected_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Setting up an expectation on a non-mock object.
-
# product = Product.new
-
# product.expects(:save).returns(true)
-
# assert_equal true, product.save
-
#
-
# @example Setting up multiple expectations on a non-mock object.
-
# product = Product.new
-
# product.expects(:valid? => true, :save => true)
-
#
-
# # exactly equivalent to
-
#
-
# product = Product.new
-
# product.expects(:valid?).returns(true)
-
# product.expects(:save).returns(true)
-
#
-
# @see Mock#expects
-
1
def expects(expected_methods_vs_return_values)
-
87
if expected_methods_vs_return_values.to_s =~ /the[^a-z]*spanish[^a-z]*inquisition/i
-
raise ExpectationErrorFactory.build('NOBODY EXPECTS THE SPANISH INQUISITION!')
-
end
-
87
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
-
end
-
87
expectation = nil
-
87
mockery = Mocha::Mockery.instance
-
87
iterator = ArgumentIterator.new(expected_methods_vs_return_values)
-
87
iterator.each { |*args|
-
87
method_name = args.shift
-
87
mockery.on_stubbing(self, method_name)
-
87
method = stubba_method.new(stubba_object, method_name)
-
87
mockery.stubba.stub(method)
-
87
expectation = mocha.expects(method_name, caller)
-
87
expectation.returns(args.shift) if args.length > 0
-
}
-
87
expectation
-
end
-
-
# Adds an expectation that the specified method may be called any number of times with any parameters.
-
#
-
# @param [Symbol,String] method_name name of stubbed method
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
-
#
-
# @overload def stubs(method_name)
-
# @overload def stubs(stubbed_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Setting up a stubbed methods on a non-mock object.
-
# product = Product.new
-
# product.stubs(:save).returns(true)
-
# assert_equal true, product.save
-
#
-
# @example Setting up multiple stubbed methods on a non-mock object.
-
# product = Product.new
-
# product.stubs(:valid? => true, :save => true)
-
#
-
# # exactly equivalent to
-
#
-
# product = Product.new
-
# product.stubs(:valid?).returns(true)
-
# product.stubs(:save).returns(true)
-
#
-
# @see Mock#stubs
-
1
def stubs(stubbed_methods_vs_return_values)
-
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
-
end
-
expectation = nil
-
mockery = Mocha::Mockery.instance
-
iterator = ArgumentIterator.new(stubbed_methods_vs_return_values)
-
iterator.each { |*args|
-
method_name = args.shift
-
mockery.on_stubbing(self, method_name)
-
method = stubba_method.new(stubba_object, method_name)
-
mockery.stubba.stub(method)
-
expectation = mocha.stubs(method_name, caller)
-
expectation.returns(args.shift) if args.length > 0
-
}
-
expectation
-
end
-
-
# Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
-
#
-
# Restores the original behaviour of the methods before they were stubbed.
-
#
-
# WARNING: If you {#unstub} a method which still has unsatisfied expectations, you may be removing the only way those expectations can be satisfied. Use {#unstub} with care.
-
#
-
# @param [Array<Symbol>] method_names names of methods to unstub.
-
#
-
# @example Stubbing and unstubbing a method on a real (non-mock) object.
-
# multiplier = Multiplier.new
-
# multiplier.double(2) # => 4
-
# multiplier.stubs(:double).raises # new behaviour defined
-
# multiplier.double(2) # => raises exception
-
# multiplier.unstub(:double) # original behaviour restored
-
# multiplier.double(2) # => 4
-
#
-
# @example Unstubbing multiple methods on a real (non-mock) object.
-
# multiplier.unstub(:double, :triple)
-
#
-
# # exactly equivalent to
-
#
-
# multiplier.unstub(:double)
-
# multiplier.unstub(:triple)
-
1
def unstub(*method_names)
-
mockery = Mocha::Mockery.instance
-
method_names.each do |method_name|
-
method = stubba_method.new(stubba_object, method_name)
-
mockery.stubba.unstub(method)
-
end
-
end
-
-
# @private
-
1
def method_exists?(method, include_public_methods = true)
-
if include_public_methods
-
return true if public_methods(include_superclass_methods = true).include?(method)
-
return true if respond_to?(method.to_sym)
-
end
-
return true if protected_methods(include_superclass_methods = true).include?(method)
-
return true if private_methods(include_superclass_methods = true).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Used as parameters for {Expectation#with} to restrict the parameter values which will match the expectation. Can be nested.
-
1
module ParameterMatchers; end
-
-
end
-
-
1
require 'mocha/parameter_matchers/object'
-
-
1
require 'mocha/parameter_matchers/all_of'
-
1
require 'mocha/parameter_matchers/any_of'
-
1
require 'mocha/parameter_matchers/any_parameters'
-
1
require 'mocha/parameter_matchers/anything'
-
1
require 'mocha/parameter_matchers/equals'
-
1
require 'mocha/parameter_matchers/has_entry'
-
1
require 'mocha/parameter_matchers/has_entries'
-
1
require 'mocha/parameter_matchers/has_key'
-
1
require 'mocha/parameter_matchers/has_value'
-
1
require 'mocha/parameter_matchers/includes'
-
1
require 'mocha/parameter_matchers/instance_of'
-
1
require 'mocha/parameter_matchers/is_a'
-
1
require 'mocha/parameter_matchers/kind_of'
-
1
require 'mocha/parameter_matchers/not'
-
1
require 'mocha/parameter_matchers/optionally'
-
1
require 'mocha/parameter_matchers/regexp_matches'
-
1
require 'mocha/parameter_matchers/responds_with'
-
1
require 'mocha/parameter_matchers/yaml_equivalent'
-
1
require 'mocha/parameter_matchers/query_string'
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if all +matchers+ match.
-
#
-
# @param [*Array<Base>] parameter_matchers parameter matchers.
-
# @return [AllOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example All parameter matchers match.
-
# object = mock()
-
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
-
# object.method_1([1, 3])
-
# # no error raised
-
#
-
# @example One of the parameter matchers does not match.
-
# object = mock()
-
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
-
# object.method_1([1, 2])
-
# # error raised, because method_1 was not called with object including 1 and 3
-
1
def all_of(*matchers)
-
AllOf.new(*matchers)
-
end
-
-
# Parameter matcher which combines a number of other matchers using a logical AND.
-
1
class AllOf < Base
-
-
# @private
-
1
def initialize(*matchers)
-
@matchers = matchers
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@matchers.all? { |matcher| matcher.to_matcher.matches?([parameter]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"all_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if any +matchers+ match.
-
#
-
# @param [*Array<Base>] parameter_matchers parameter matchers.
-
# @return [AnyOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example One parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(1)
-
# # no error raised
-
#
-
# @example The other parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(3)
-
# # no error raised
-
#
-
# @example Neither parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(2)
-
# # error raised, because method_1 was not called with 1 or 3
-
1
def any_of(*matchers)
-
AnyOf.new(*matchers)
-
end
-
-
# Parameter matcher which combines a number of other matchers using a logical OR.
-
1
class AnyOf < Base
-
-
# @private
-
1
def initialize(*matchers)
-
@matchers = matchers
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@matchers.any? { |matcher| matcher.to_matcher.matches?([parameter]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"any_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any parameters. This is used as the default for a newly built expectation.
-
#
-
# @return [AnyParameters] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Any parameters will match.
-
# object = mock()
-
# object.expects(:method_1).with(any_parameters)
-
# object.method_1(1, 2, 3, 4)
-
# # no error raised
-
#
-
# object = mock()
-
# object.expects(:method_1).with(any_parameters)
-
# object.method_1(5, 6, 7, 8, 9, 0)
-
# # no error raised
-
1
def any_parameters
-
AnyParameters.new
-
end
-
-
# Parameter matcher which always matches whatever the parameters.
-
1
class AnyParameters < Base
-
-
# @private
-
1
def matches?(available_parameters)
-
7
while available_parameters.length > 0 do
-
4
available_parameters.shift
-
end
-
7
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"any_parameters"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object.
-
#
-
# @return [Anything] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Any object will match.
-
# object = mock()
-
# object.expects(:method_1).with(anything)
-
# object.method_1('foo')
-
# object.method_1(789)
-
# object.method_1(:bar)
-
# # no error raised
-
1
def anything
-
Anything.new
-
end
-
-
# Parameter matcher which always matches a single parameter.
-
1
class Anything < Base
-
-
# @private
-
1
def matches?(available_parameters)
-
available_parameters.shift
-
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"anything"
-
end
-
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}.
-
1
class Base
-
-
# @private
-
1
def to_matcher
-
7
self
-
end
-
-
# A shorthand way of combining two matchers when both must match.
-
#
-
# Returns a new {AllOf} parameter matcher combining two matchers using a logical AND.
-
#
-
# This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used.
-
#
-
# @param [Base] matcher parameter matcher.
-
# @return [AllOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Alternative ways to combine matchers with a logical AND.
-
# object = mock()
-
# object.expects(:run).with(all_of(has_key(:foo), has_key(:bar)))
-
# object.run(:foo => 'foovalue', :bar => 'barvalue')
-
#
-
# # is exactly equivalent to
-
#
-
# object.expects(:run).with(has_key(:foo) & has_key(:bar))
-
# object.run(:foo => 'foovalue', :bar => 'barvalue)
-
1
def &(matcher)
-
AllOf.new(self, matcher)
-
end
-
-
# A shorthand way of combining two matchers when at least one must match.
-
#
-
# Returns a new +AnyOf+ parameter matcher combining two matchers using a logical OR.
-
#
-
# This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used.
-
#
-
# @param [Base] matcher parameter matcher.
-
# @return [AnyOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Alternative ways to combine matchers with a logical OR.
-
# object = mock()
-
# object.expects(:run).with(any_of(has_key(:foo), has_key(:bar)))
-
# object.run(:foo => 'foovalue')
-
#
-
# # is exactly equivalent to
-
#
-
# object.expects(:run).with(has_key(:foo) | has_key(:bar))
-
# object.run(:foo => 'foovalue')
-
#
-
# @example Using an explicit {Equals} matcher in combination with {#|}.
-
# object.expects(:run).with(equals(1) | equals(2))
-
# object.run(1) # passes
-
# object.run(2) # passes
-
# object.run(3) # fails
-
1
def |(matcher)
-
AnyOf.new(self, matcher)
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any +Object+ equalling +value+.
-
#
-
# @param [Object] value expected value.
-
# @return [Equals] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Object#==
-
#
-
# @example Actual parameter equals expected parameter.
-
# object = mock()
-
# object.expects(:method_1).with(equals(2))
-
# object.method_1(2)
-
# # no error raised
-
#
-
# @example Actual parameter does not equal expected parameter.
-
# object = mock()
-
# object.expects(:method_1).with(equals(2))
-
# object.method_1(3)
-
# # error raised, because method_1 was not called with an +Object+ that equals 3
-
1
def equals(value)
-
Equals.new(value)
-
end
-
-
# Parameter matcher which matches when actual parameter equals expected value.
-
1
class Equals < Base
-
-
# @private
-
1
def initialize(value)
-
288
@value = value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
280
parameter = available_parameters.shift
-
280
parameter == @value
-
end
-
-
# @private
-
1
def mocha_inspect
-
@value.mocha_inspect
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'mocha/parameter_matchers/all_of'
-
1
require 'mocha/parameter_matchers/has_entry'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing all +entries+.
-
#
-
# @param [Hash] entries expected +Hash+ entries.
-
# @return [HasEntries] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains all expected entries.
-
# object = mock()
-
# object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2))
-
# object.method_1('key_1' => 1, 'key_2' => 2, 'key_3' => 3)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain all expected entries.
-
# object = mock()
-
# object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2))
-
# object.method_1('key_1' => 1, 'key_2' => 99)
-
# # error raised, because method_1 was not called with Hash containing entries: 'key_1' => 1, 'key_2' => 2
-
1
def has_entries(entries)
-
HasEntries.new(entries)
-
end
-
-
# Parameter matcher which matches when actual parameter contains all expected +Hash+ entries.
-
1
class HasEntries < Base
-
-
# @private
-
1
def initialize(entries)
-
@entries = entries
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
has_entry_matchers = @entries.map { |key, value| HasEntry.new(key, value) }
-
AllOf.new(*has_entry_matchers).matches?([parameter])
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_entries(#{@entries.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing entry with +key+ and +value+.
-
#
-
# @overload def has_entry(key, value)
-
# @param [Object] key key for entry.
-
# @param [Object] value value for entry.
-
# @overload def has_entry(single_entry_hash)
-
# @param [Hash] single_entry_hash +Hash+ with single entry.
-
# @raise [ArgumentError] if +single_entry_hash+ does not contain exactly one entry.
-
#
-
# @return [HasEntry] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains expected entry supplied as key and value.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1', 1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter contains expected entry supplied as +Hash+ entry.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1' => 1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain expected entry supplied as key and value.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1', 1))
-
# object.method_1('key_1' => 2, 'key_2' => 1)
-
# # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
-
#
-
# @example Actual parameter does not contain expected entry supplied as +Hash+ entry.
-
#
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1' => 1))
-
# object.method_1('key_1' => 2, 'key_2' => 1)
-
# # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
-
1
def has_entry(*options)
-
key, value = options.shift, options.shift
-
if key.is_a?(Hash)
-
case key.length
-
when 0
-
raise ArgumentError.new("Argument has no entries.")
-
when 1
-
key, value = key.to_a.flatten
-
else
-
raise ArgumentError.new("Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.")
-
end
-
end
-
HasEntry.new(key, value)
-
end
-
-
# Parameter matcher which matches when actual parameter contains expected +Hash+ entry.
-
1
class HasEntry < Base
-
-
# @private
-
1
def initialize(key, value)
-
@key, @value = key, value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:keys) && parameter.respond_to?(:[])
-
matching_keys = parameter.keys.select { |key| @key.to_matcher.matches?([key]) }
-
matching_keys.any? { |key| @value.to_matcher.matches?([parameter[key]]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_entry(#{@key.mocha_inspect} => #{@value.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing +key+.
-
#
-
# @param [Object] key expected key.
-
# @return [HasKey] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains entry with expected key.
-
# object = mock()
-
# object.expects(:method_1).with(has_key('key_1'))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain entry with expected key.
-
# object = mock()
-
# object.expects(:method_1).with(has_key('key_1'))
-
# object.method_1('key_2' => 2)
-
# # error raised, because method_1 was not called with Hash containing key: 'key_1'
-
1
def has_key(key)
-
HasKey.new(key)
-
end
-
-
# Parameter matcher which matches when actual parameter contains +Hash+ entry with expected key.
-
1
class HasKey < Base
-
-
# @private
-
1
def initialize(key)
-
@key = key
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:keys)
-
parameter.keys.any? { |key| @key.to_matcher.matches?([key]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_key(#{@key.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing +value+.
-
#
-
# @param [Object] value expected value.
-
# @return [HasValue] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains entry with expected value.
-
# object = mock()
-
# object.expects(:method_1).with(has_value(1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain entry with expected value.
-
# object = mock()
-
# object.expects(:method_1).with(has_value(1))
-
# object.method_1('key_2' => 2)
-
# # error raised, because method_1 was not called with Hash containing value: 1
-
1
def has_value(value)
-
HasValue.new(value)
-
end
-
-
# Parameter matcher which matches when actual parameter contains +Hash+ entry with expected value.
-
1
class HasValue < Base
-
-
# @private
-
1
def initialize(value)
-
@value = value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:values)
-
parameter.values.any? { |value| @value.to_matcher.matches?([value]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_value(#{@value.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that responds with +true+ to +include?(item)+.
-
#
-
# @param [Object] item expected item.
-
# @return [Includes] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter includes item.
-
# object = mock()
-
# object.expects(:method_1).with(includes('foo'))
-
# object.method_1(['foo', 'bar'])
-
# # no error raised
-
#
-
# @example Actual parameter does not include item.
-
# object.method_1(['baz'])
-
# # error raised, because ['baz'] does not include 'foo'.
-
1
def includes(item)
-
Includes.new(item)
-
end
-
-
# Parameter matcher which matches when actual parameter includes expected value.
-
1
class Includes < Base
-
-
# @private
-
1
def initialize(item)
-
@item = item
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:include?)
-
return parameter.include?(@item)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"includes(#{@item.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that is an instance of +klass+
-
#
-
# @param [Class] klass expected class.
-
# @return [InstanceOf] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#instance_of?
-
#
-
# @example Actual parameter is an instance of +String+.
-
# object = mock()
-
# object.expects(:method_1).with(instance_of(String))
-
# object.method_1('string')
-
# # no error raised
-
#
-
# @example Actual parameter is not an instance of +String+.
-
# object = mock()
-
# object.expects(:method_1).with(instance_of(String))
-
# object.method_1(99)
-
# # error raised, because method_1 was not called with an instance of String
-
1
def instance_of(klass)
-
InstanceOf.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is an instance of the specified class.
-
1
class InstanceOf < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.instance_of?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"instance_of(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that is a +klass+.
-
#
-
# @param [Class] klass expected class.
-
# @return [IsA] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#is_a?
-
#
-
# @example Actual parameter is a +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(is_a(Integer))
-
# object.method_1(99)
-
# # no error raised
-
#
-
# @example Actual parameter is not a +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(is_a(Integer))
-
# object.method_1('string')
-
# # error raised, because method_1 was not called with an Integer
-
1
def is_a(klass)
-
IsA.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is a specific class.
-
1
class IsA < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.is_a?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"is_a(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any +Object+ that is a kind of +klass+.
-
#
-
# @param [Class] klass expected class.
-
# @return [KindOf] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#kind_of?
-
#
-
# @example Actual parameter is a kind of +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(kind_of(Integer))
-
# object.method_1(99)
-
# # no error raised
-
#
-
# @example Actual parameter is not a kind of +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(kind_of(Integer))
-
# object.method_1('string')
-
# # error raised, because method_1 was not called with a kind of Integer
-
1
def kind_of(klass)
-
KindOf.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is a kind of specified class.
-
1
class KindOf < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.kind_of?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"kind_of(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if +matcher+ does *not* match.
-
#
-
# @param [Base] matcher matcher whose logic to invert.
-
# @return [Not] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter does not include the value +1+.
-
# object = mock()
-
# object.expects(:method_1).with(Not(includes(1)))
-
# object.method_1([0, 2, 3])
-
# # no error raised
-
#
-
# @example Actual parameter does include the value +1+.
-
# object = mock()
-
# object.expects(:method_1).with(Not(includes(1)))
-
# object.method_1([0, 1, 2, 3])
-
# # error raised, because method_1 was not called with object not including 1
-
1
def Not(matcher)
-
Not.new(matcher)
-
end
-
-
# Parameter matcher which inverts the logic of the specified matcher using a logical NOT operation.
-
1
class Not < Base
-
-
# @private
-
1
def initialize(matcher)
-
@matcher = matcher
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
!@matcher.matches?([parameter])
-
end
-
-
# @private
-
1
def mocha_inspect
-
"Not(#{@matcher.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/equals'
-
-
1
module Mocha
-
-
1
module ObjectMethods
-
# @private
-
1
def to_matcher
-
288
Mocha::ParameterMatchers::Equals.new(self)
-
end
-
end
-
-
end
-
-
# @private
-
1
class Object
-
1
include Mocha::ObjectMethods
-
end
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches optional parameters if available.
-
#
-
# @param [*Array<Base>] matchers matchers for optional parameters.
-
# @return [Optionally] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Only the two required parameters are supplied and they both match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2)
-
# # no error raised
-
#
-
# @example Both required parameters and one of the optional parameters are supplied and they all match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3)
-
# # no error raised
-
#
-
# @example Both required parameters and both of the optional parameters are supplied and they all match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3, 4)
-
# # no error raised
-
#
-
# @example One of the actual optional parameters does not match the expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3, 5)
-
# # error raised, because optional parameters did not match
-
1
def optionally(*matchers)
-
Optionally.new(*matchers)
-
end
-
-
# Parameter matcher which allows optional parameters to be specified.
-
1
class Optionally < Base
-
-
# @private
-
1
def initialize(*parameters)
-
@matchers = parameters.map { |parameter| parameter.to_matcher }
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
index = 0
-
while (available_parameters.length > 0) && (index < @matchers.length) do
-
matcher = @matchers[index]
-
return false unless matcher.matches?(available_parameters)
-
index += 1
-
end
-
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"optionally(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'uri'
-
-
1
module Mocha
-
1
module ParameterMatchers
-
-
# Matches a URI without regard to the ordering of parameters in the query string.
-
#
-
# @param [String] uri URI to match.
-
# @return [QueryStringMatches] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual URI has equivalent query string.
-
# object = mock()
-
# object.expects(:method_1).with(has_equivalent_query_string('http://example.com/foo?a=1&b=2))
-
# object.method_1('http://example.com/foo?b=2&a=1')
-
# # no error raised
-
#
-
# @example Actual URI does not have equivalent query string.
-
# object = mock()
-
# object.expects(:method_1).with(has_equivalent_query_string('http://example.com/foo?a=1&b=2))
-
# object.method_1('http://example.com/foo?a=1&b=3')
-
# # error raised, because the query parameters were different
-
1
def has_equivalent_query_string(uri)
-
QueryStringMatches.new(uri)
-
end
-
-
# Parameter matcher which matches URIs with equivalent query strings.
-
1
class QueryStringMatches < Base
-
-
# @private
-
1
def initialize(uri)
-
@uri = URI.parse(uri)
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
actual = explode(URI.parse(available_parameters.shift))
-
expected = explode(@uri)
-
actual == expected
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_equivalent_query_string(#{@uri.mocha_inspect})"
-
end
-
-
1
private
-
# @private
-
1
def explode(uri)
-
query_hash = (uri.query || '').split('&').inject({}){ |h, kv| h.merge(Hash[*kv.split('=')]) }
-
URI::Generic::COMPONENT.inject({}){ |h, k| h.merge(k => uri.__send__(k)) }.merge(:query => query_hash)
-
end
-
-
end
-
end
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that matches +regexp+.
-
#
-
# @param [Regexp] regexp regular expression to match.
-
# @return [RegexpMatches] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter is matched by specified regular expression.
-
# object = mock()
-
# object.expects(:method_1).with(regexp_matches(/e/))
-
# object.method_1('hello')
-
# # no error raised
-
#
-
# @example Actual parameter is not matched by specified regular expression.
-
# object = mock()
-
# object.expects(:method_1).with(regexp_matches(/a/))
-
# object.method_1('hello')
-
# # error raised, because method_1 was not called with a parameter that matched the
-
# # regular expression
-
1
def regexp_matches(regexp)
-
RegexpMatches.new(regexp)
-
end
-
-
# Parameter matcher which matches if specified regular expression matches actual paramter.
-
1
class RegexpMatches < Base
-
-
# @private
-
1
def initialize(regexp)
-
@regexp = regexp
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:=~)
-
parameter =~ @regexp
-
end
-
-
# @private
-
1
def mocha_inspect
-
"regexp_matches(#{@regexp.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'yaml'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck.
-
#
-
# @param [Symbol] message method to invoke.
-
# @param [Object] result expected result of sending +message+.
-
# @return [RespondsWith] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter responds with "FOO" when :upcase is invoked.
-
# object = mock()
-
# object.expects(:method_1).with(responds_with(:upcase, "FOO"))
-
# object.method_1("foo")
-
# # no error raised, because "foo".upcase == "FOO"
-
#
-
# @example Actual parameter does not respond with "FOO" when :upcase is invoked.
-
# object = mock()
-
# object.expects(:method_1).with(responds_with(:upcase, "BAR"))
-
# object.method_1("foo")
-
# # error raised, because "foo".upcase != "BAR"
-
1
def responds_with(message, result)
-
RespondsWith.new(message, result)
-
end
-
-
# Parameter matcher which matches if actual parameter returns expected result when specified method is invoked.
-
1
class RespondsWith < Base
-
-
# @private
-
1
def initialize(message, result)
-
@message, @result = message, result
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.__send__(@message) == @result
-
end
-
-
# @private
-
1
def mocha_inspect
-
"responds_with(#{@message.mocha_inspect}, #{@result.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'yaml'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any YAML that represents the specified +object+
-
#
-
# @param [Object] object object whose YAML to compare.
-
# @return [YamlEquivalent] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter is YAML equivalent of specified +object+.
-
# object = mock()
-
# object.expects(:method_1).with(yaml_equivalent(1, 2, 3))
-
# object.method_1("--- \n- 1\n- 2\n- 3\n")
-
# # no error raised
-
#
-
# @example Actual parameter is not YAML equivalent of specified +object+.
-
# object = mock()
-
# object.expects(:method_1).with(yaml_equivalent(1, 2, 3))
-
# object.method_1("--- \n- 1\n- 2\n")
-
# # error raised, because method_1 was not called with YAML representing the specified Array
-
1
def yaml_equivalent(object)
-
YamlEquivalent.new(object)
-
end
-
-
# Parameter matcher which matches if actual parameter is YAML equivalent of specified object.
-
1
class YamlEquivalent < Base
-
-
# @private
-
1
def initialize(object)
-
@object = object
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@object == YAML.load(parameter)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"yaml_equivalent(#{@object.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/inspect'
-
1
require 'mocha/parameter_matchers'
-
-
1
module Mocha
-
-
1
class ParametersMatcher
-
-
1
def initialize(expected_parameters = [ParameterMatchers::AnyParameters.new], &matching_block)
-
193
@expected_parameters, @matching_block = expected_parameters, matching_block
-
end
-
-
1
def match?(actual_parameters = [])
-
119
if @matching_block
-
return @matching_block.call(*actual_parameters)
-
else
-
119
return parameters_match?(actual_parameters)
-
end
-
end
-
-
1
def parameters_match?(actual_parameters)
-
406
matchers.all? { |matcher| matcher.matches?(actual_parameters) } && (actual_parameters.length == 0)
-
end
-
-
1
def mocha_inspect
-
signature = matchers.mocha_inspect
-
signature = signature.gsub(/^\[|\]$/, '')
-
signature = signature.gsub(/^\{|\}$/, '') if matchers.length == 1
-
"(#{signature})"
-
end
-
-
1
def matchers
-
414
@expected_parameters.map { |parameter| parameter.to_matcher }
-
end
-
-
end
-
-
end
-
1
require 'mocha/single_return_value'
-
-
1
module Mocha
-
-
1
class ReturnValues
-
-
1
def self.build(*values)
-
12
new(*values.map { |value| SingleReturnValue.new(value) })
-
end
-
-
1
attr_accessor :values
-
-
1
def initialize(*values)
-
112
@values = values
-
end
-
-
1
def next
-
99
case @values.length
-
when 0 then nil
-
6
when 1 then @values.first.evaluate
-
else @values.shift.evaluate
-
end
-
end
-
-
1
def +(other)
-
6
self.class.new(*(@values + other.values))
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Used to constrain the order in which expectations can occur.
-
#
-
# @see API#sequence
-
# @see Expectation#in_sequence
-
1
class Sequence
-
-
# @private
-
1
class InSequenceOrderingConstraint
-
-
1
def initialize(sequence, index)
-
@sequence, @index = sequence, index
-
end
-
-
1
def allows_invocation_now?
-
@sequence.satisfied_to_index?(@index)
-
end
-
-
1
def mocha_inspect
-
"in sequence #{@sequence.mocha_inspect}"
-
end
-
-
end
-
-
# @private
-
1
def initialize(name)
-
@name = name
-
@expectations = []
-
end
-
-
# @private
-
1
def constrain_as_next_in_sequence(expectation)
-
index = @expectations.length
-
@expectations << expectation
-
expectation.add_ordering_constraint(InSequenceOrderingConstraint.new(self, index))
-
end
-
-
# @private
-
1
def satisfied_to_index?(index)
-
@expectations[0...index].all? { |expectation| expectation.satisfied? }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@name.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'mocha/is_a'
-
-
1
module Mocha
-
-
1
class SingleReturnValue
-
-
1
def initialize(value)
-
6
@value = value
-
end
-
-
1
def evaluate
-
6
@value
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class SingleYield
-
-
1
attr_reader :parameters
-
-
1
def initialize(*parameters)
-
@parameters = parameters
-
end
-
-
1
def each
-
yield(@parameters)
-
end
-
-
end
-
-
end
-
-
1
module Mocha
-
-
# A state machine that is used to constrain the order of invocations.
-
# An invocation can be constrained to occur when a state {#is}, or {#is_not}, active.
-
1
class StateMachine
-
-
# Provides a mechanism to change the state of a {StateMachine} at some point in the future.
-
1
class State
-
-
# @private
-
1
def initialize(state_machine, state)
-
@state_machine, @state = state_machine, state
-
end
-
-
# @private
-
1
def activate
-
@state_machine.current_state = @state
-
end
-
-
# @private
-
1
def active?
-
@state_machine.current_state == @state
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@state_machine.name} is #{@state.mocha_inspect}"
-
end
-
-
end
-
-
# Provides the ability to determine whether a {StateMachine} is in a specified state at some point in the future.
-
1
class StatePredicate
-
-
# @private
-
1
def initialize(state_machine, state)
-
@state_machine, @state = state_machine, state
-
end
-
-
# @private
-
1
def active?
-
@state_machine.current_state != @state
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@state_machine.name} is not #{@state.mocha_inspect}"
-
end
-
-
end
-
-
# @private
-
1
attr_reader :name
-
-
# @private
-
1
attr_accessor :current_state
-
-
# @private
-
1
def initialize(name)
-
@name = name
-
@current_state = nil
-
end
-
-
# Put the {StateMachine} into the state specified by +initial_state_name+.
-
#
-
# @param [String] initial_state_name name of initial state
-
# @return [StateMachine] state machine, thereby allowing invocations of other {StateMachine} methods to be chained.
-
1
def starts_as(initial_state_name)
-
become(initial_state_name)
-
self
-
end
-
-
# Put the {StateMachine} into the +next_state_name+.
-
#
-
# @param [String] next_state_name name of new state
-
1
def become(next_state_name)
-
@current_state = next_state_name
-
end
-
-
# Provides a mechanism to change the {StateMachine} into the state specified by +state_name+ at some point in the future.
-
#
-
# Or provides a mechanism to determine whether the {StateMachine} is in the state specified by +state_name+ at some point in the future.
-
#
-
# @param [String] state_name name of new state
-
# @return [State] state which, when activated, will change the {StateMachine} into the state with the specified +state_name+.
-
1
def is(state_name)
-
State.new(self, state_name)
-
end
-
-
# Provides a mechanism to determine whether the {StateMachine} is not in the state specified by +state_name+ at some point in the future.
-
1
def is_not(state_name)
-
StatePredicate.new(self, state_name)
-
end
-
-
# @private
-
1
def mocha_inspect
-
if @current_state
-
"#{@name} is #{@current_state.mocha_inspect}"
-
else
-
"#{@name} has no current state"
-
end
-
end
-
-
end
-
-
end
-
1
require 'mocha/backtrace_filter'
-
-
1
module Mocha
-
-
# Exception raised when stubbing a particular method is not allowed.
-
#
-
# @see Configuration.prevent
-
1
class StubbingError < StandardError
-
-
# @private
-
1
def initialize(message = nil, backtrace = [])
-
super(message)
-
filter = BacktraceFilter.new
-
set_backtrace(filter.filtered(backtrace))
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Thrower
-
-
1
def initialize(tag, object = nil)
-
@tag, @object = tag, object
-
end
-
-
1
def evaluate
-
throw @tag, @object
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Exception raised when an unexpected method is invoked
-
1
class UnexpectedInvocation
-
-
# @private
-
1
def initialize(mock, symbol, *arguments)
-
@mock = mock
-
@method_matcher = MethodMatcher.new(symbol)
-
@parameters_matcher = ParametersMatcher.new(arguments)
-
end
-
-
# @private
-
1
def to_s
-
method_signature = "#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
-
"unexpected invocation: #{method_signature}\n"
-
end
-
-
end
-
-
end
-
1
require 'mocha/no_yields'
-
1
require 'mocha/single_yield'
-
1
require 'mocha/multiple_yields'
-
-
1
module Mocha
-
-
1
class YieldParameters
-
-
1
def initialize
-
100
@parameter_groups = []
-
end
-
-
1
def next_invocation
-
case @parameter_groups.length
-
when 0 then NoYields.new
-
when 1 then @parameter_groups.first
-
else @parameter_groups.shift
-
end
-
end
-
-
1
def add(*parameters)
-
@parameter_groups << SingleYield.new(*parameters)
-
end
-
-
1
def multiple_add(*parameter_groups)
-
@parameter_groups << MultipleYields.new(*parameter_groups)
-
end
-
-
end
-
-
end
-
#
-
# = base64.rb: methods for base64-encoding and -decoding strings
-
#
-
-
# The Base64 module provides for the encoding (#encode64, #strict_encode64,
-
# #urlsafe_encode64) and decoding (#decode64, #strict_decode64,
-
# #urlsafe_decode64) of binary data using a Base64 representation.
-
#
-
# == Example
-
#
-
# A simple encoding and decoding.
-
#
-
# require "base64"
-
#
-
# enc = Base64.encode64('Send reinforcements')
-
# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
-
# plain = Base64.decode64(enc)
-
# # -> "Send reinforcements"
-
#
-
# The purpose of using base64 to encode data is that it translates any
-
# binary data into purely printable characters.
-
-
1
module Base64
-
1
module_function
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with RFC 2045.
-
# Line feeds are added to every 60 encoded charactors.
-
#
-
# require 'base64'
-
# Base64.encode64("Now is the time for all good coders\nto learn Ruby")
-
#
-
# <i>Generates:</i>
-
#
-
# Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
-
# UnVieQ==
-
1
def encode64(bin)
-
[bin].pack("m")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with RFC 2045.
-
# Characters outside the base alphabet are ignored.
-
#
-
# require 'base64'
-
# str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
-
# 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
-
# 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
-
# puts Base64.decode64(str)
-
#
-
# <i>Generates:</i>
-
#
-
# This is line one
-
# This is line two
-
# This is line three
-
# And so on...
-
1
def decode64(str)
-
str.unpack("m").first
-
end
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with RFC 4648.
-
# No line feeds are added.
-
1
def strict_encode64(bin)
-
[bin].pack("m0")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with RFC 4648.
-
# ArgumentError is raised if +str+ is incorrectly padded or contains
-
# non-alphabet characters. Note that CR or LF are also rejected.
-
1
def strict_decode64(str)
-
str.unpack("m0").first
-
end
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with ``Base 64 Encoding with URL and Filename Safe
-
# Alphabet'' in RFC 4648.
-
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
-
1
def urlsafe_encode64(bin)
-
strict_encode64(bin).tr("+/", "-_")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with ``Base 64 Encoding with URL and Filename Safe
-
# Alphabet'' in RFC 4648.
-
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
-
1
def urlsafe_decode64(str)
-
strict_decode64(str.tr("-_", "+/"))
-
end
-
end
-
# = delegate -- Support for the Delegation Pattern
-
#
-
# Documentation by James Edward Gray II and Gavin Sinclair
-
-
##
-
# This library provides three different ways to delegate method calls to an
-
# object. The easiest to use is SimpleDelegator. Pass an object to the
-
# constructor and all methods supported by the object will be delegated. This
-
# object can be changed later.
-
#
-
# Going a step further, the top level DelegateClass method allows you to easily
-
# setup delegation through class inheritance. This is considerably more
-
# flexible and thus probably the most common use for this library.
-
#
-
# Finally, if you need full control over the delegation scheme, you can inherit
-
# from the abstract class Delegator and customize as needed. (If you find
-
# yourself needing this control, have a look at Forwardable which is also in
-
# the standard library. It may suit your needs better.)
-
#
-
# SimpleDelegator's implementation serves as a nice example if the use of
-
# Delegator:
-
#
-
# class SimpleDelegator < Delegator
-
# def initialize(obj)
-
# super # pass obj to Delegator constructor, required
-
# @delegate_sd_obj = obj # store obj for future use
-
# end
-
#
-
# def __getobj__
-
# @delegate_sd_obj # return object we are delegating to, required
-
# end
-
#
-
# def __setobj__(obj)
-
# @delegate_sd_obj = obj # change delegation object,
-
# # a feature we're providing
-
# end
-
# end
-
#
-
# == Notes
-
#
-
# Be advised, RDoc will not detect delegated methods.
-
#
-
1
class Delegator < BasicObject
-
1
kernel = ::Kernel.dup
-
1
kernel.class_eval do
-
1
[:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m|
-
8
undef_method m
-
end
-
end
-
1
include kernel
-
-
# :stopdoc:
-
1
def self.const_missing(n)
-
1
::Object.const_get(n)
-
end
-
# :startdoc:
-
-
#
-
# Pass in the _obj_ to delegate method calls to. All methods supported by
-
# _obj_ will be delegated to.
-
#
-
1
def initialize(obj)
-
__setobj__(obj)
-
end
-
-
#
-
# Handles the magic of delegation through \_\_getobj\_\_.
-
#
-
1
def method_missing(m, *args, &block)
-
target = self.__getobj__
-
begin
-
target.respond_to?(m) ? target.__send__(m, *args, &block) : super(m, *args, &block)
-
ensure
-
$@.delete_if {|t| %r"\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:"o =~ t} if $@
-
end
-
end
-
-
#
-
# Checks for a method provided by this the delegate object by forwarding the
-
# call through \_\_getobj\_\_.
-
#
-
1
def respond_to_missing?(m, include_private)
-
r = self.__getobj__.respond_to?(m, include_private)
-
if r && include_private && !self.__getobj__.respond_to?(m, false)
-
warn "#{caller(3)[0]}: delegator does not forward private method \##{m}"
-
return false
-
end
-
r
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ methods.
-
#
-
1
def methods
-
__getobj__.methods | super
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ public methods.
-
#
-
1
def public_methods(all=true)
-
__getobj__.public_methods(all) | super
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ protected methods.
-
#
-
1
def protected_methods(all=true)
-
__getobj__.protected_methods(all) | super
-
end
-
-
# Note: no need to specialize private_methods, since they are not forwarded
-
-
#
-
# Returns true if two objects are considered of equal value.
-
#
-
1
def ==(obj)
-
return true if obj.equal?(self)
-
self.__getobj__ == obj
-
end
-
-
#
-
# Returns true if two objects are not considered of equal value.
-
#
-
1
def !=(obj)
-
return false if obj.equal?(self)
-
__getobj__ != obj
-
end
-
-
1
def !
-
!__getobj__
-
end
-
-
#
-
# This method must be overridden by subclasses and should return the object
-
# method calls are being delegated to.
-
#
-
1
def __getobj__
-
raise NotImplementedError, "need to define `__getobj__'"
-
end
-
-
#
-
# This method must be overridden by subclasses and change the object delegate
-
# to _obj_.
-
#
-
1
def __setobj__(obj)
-
raise NotImplementedError, "need to define `__setobj__'"
-
end
-
-
#
-
# Serialization support for the object returned by \_\_getobj\_\_.
-
#
-
1
def marshal_dump
-
ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
-
[
-
:__v2__,
-
ivars, ivars.map{|var| instance_variable_get(var)},
-
__getobj__
-
]
-
end
-
-
#
-
# Reinitializes delegation from a serialized object.
-
#
-
1
def marshal_load(data)
-
version, vars, values, obj = data
-
if version == :__v2__
-
vars.each_with_index{|var, i| instance_variable_set(var, values[i])}
-
__setobj__(obj)
-
else
-
__setobj__(data)
-
end
-
end
-
-
1
def initialize_clone(obj) # :nodoc:
-
self.__setobj__(obj.__getobj__.clone)
-
end
-
1
def initialize_dup(obj) # :nodoc:
-
self.__setobj__(obj.__getobj__.dup)
-
end
-
1
private :initialize_clone, :initialize_dup
-
-
##
-
# :method: trust
-
# Trust both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: untrust
-
# Untrust both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: taint
-
# Taint both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: untaint
-
# Untaint both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: freeze
-
# Freeze both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
1
[:trust, :untrust, :taint, :untaint, :freeze].each do |method|
-
5
define_method method do
-
__getobj__.send(method)
-
super()
-
end
-
end
-
-
1
@delegator_api = self.public_instance_methods
-
1
def self.public_api # :nodoc:
-
1
@delegator_api
-
end
-
end
-
-
##
-
# A concrete implementation of Delegator, this class provides the means to
-
# delegate all supported method calls to the object passed into the constructor
-
# and even to change the object being delegated to at a later time with
-
# #__setobj__.
-
#
-
# Here's a simple example that takes advantage of the fact that
-
# SimpleDelegator's delegation object can be changed at any time.
-
#
-
# class Stats
-
# def initialize
-
# @source = SimpleDelegator.new([])
-
# end
-
#
-
# def stats(records)
-
# @source.__setobj__(records)
-
#
-
# "Elements: #{@source.size}\n" +
-
# " Non-Nil: #{@source.compact.size}\n" +
-
# " Unique: #{@source.uniq.size}\n"
-
# end
-
# end
-
#
-
# s = Stats.new
-
# puts s.stats(%w{James Edward Gray II})
-
# puts
-
# puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
-
#
-
# Prints:
-
#
-
# Elements: 4
-
# Non-Nil: 4
-
# Unique: 4
-
#
-
# Elements: 8
-
# Non-Nil: 7
-
# Unique: 6
-
#
-
1
class SimpleDelegator<Delegator
-
# Returns the current object method calls are being delegated to.
-
1
def __getobj__
-
@delegate_sd_obj
-
end
-
-
#
-
# Changes the delegate object to _obj_.
-
#
-
# It's important to note that this does *not* cause SimpleDelegator's methods
-
# to change. Because of this, you probably only want to change delegation
-
# to objects of the same type as the original delegate.
-
#
-
# Here's an example of changing the delegation object.
-
#
-
# names = SimpleDelegator.new(%w{James Edward Gray II})
-
# puts names[1] # => Edward
-
# names.__setobj__(%w{Gavin Sinclair})
-
# puts names[1] # => Sinclair
-
#
-
1
def __setobj__(obj)
-
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
-
@delegate_sd_obj = obj
-
end
-
end
-
-
# :stopdoc:
-
1
def Delegator.delegating_block(mid)
-
171
lambda do |*args, &block|
-
target = self.__getobj__
-
begin
-
target.__send__(mid, *args, &block)
-
ensure
-
$@.delete_if {|t| /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:/o =~ t} if $@
-
end
-
end
-
end
-
# :startdoc:
-
-
#
-
# The primary interface to this library. Use to setup delegation when defining
-
# your class.
-
#
-
# class MyClass < DelegateClass(ClassToDelegateTo) # Step 1
-
# def initialize
-
# super(obj_of_ClassToDelegateTo) # Step 2
-
# end
-
# end
-
#
-
# Here's a sample of use from Tempfile which is really a File object with a
-
# few special rules about storage location and when the File should be
-
# deleted. That makes for an almost textbook perfect example of how to use
-
# delegation.
-
#
-
# class Tempfile < DelegateClass(File)
-
# # constant and class member data initialization...
-
#
-
# def initialize(basename, tmpdir=Dir::tmpdir)
-
# # build up file path/name in var tmpname...
-
#
-
# @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
-
#
-
# # ...
-
#
-
# super(@tmpfile)
-
#
-
# # below this point, all methods of File are supported...
-
# end
-
#
-
# # ...
-
# end
-
#
-
1
def DelegateClass(superclass)
-
1
klass = Class.new(Delegator)
-
1
methods = superclass.instance_methods
-
1
methods -= ::Delegator.public_api
-
1
methods -= [:to_s,:inspect,:=~,:!~,:===]
-
1
klass.module_eval do
-
1
def __getobj__ # :nodoc:
-
@delegate_dc_obj
-
end
-
1
def __setobj__(obj) # :nodoc:
-
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
-
@delegate_dc_obj = obj
-
end
-
1
methods.each do |method|
-
171
define_method(method, Delegator.delegating_block(method))
-
end
-
end
-
1
klass.define_singleton_method :public_instance_methods do |all=true|
-
super(all) - superclass.protected_instance_methods
-
end
-
1
klass.define_singleton_method :protected_instance_methods do |all=true|
-
super(all) | superclass.protected_instance_methods
-
end
-
1
return klass
-
end
-
-
# :enddoc:
-
-
1
if __FILE__ == $0
-
class ExtArray<DelegateClass(Array)
-
def initialize()
-
super([])
-
end
-
end
-
-
ary = ExtArray.new
-
p ary.class
-
ary.push 25
-
p ary
-
ary.push 42
-
ary.each {|x| p x}
-
-
foo = Object.new
-
def foo.test
-
25
-
end
-
def foo.iter
-
yield self
-
end
-
def foo.error
-
raise 'this is OK'
-
end
-
foo2 = SimpleDelegator.new(foo)
-
p foo2
-
foo2.instance_eval{print "foo\n"}
-
p foo.test == foo2.test # => true
-
p foo2.iter{[55,true]} # => true
-
foo2.error # raise error!
-
end
-
# logger.rb - simple logging utility
-
# Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
-
#
-
# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair
-
# License::
-
# You can redistribute it and/or modify it under the same terms of Ruby's
-
# license; either the dual license version in 2003, or any later version.
-
# Revision:: $Id: logger.rb 31641 2011-05-19 00:07:25Z nobu $
-
#
-
# A simple system for logging messages. See Logger for more documentation.
-
-
1
require 'monitor'
-
-
# == Description
-
#
-
# The Logger class provides a simple but sophisticated logging utility that
-
# you can use to output messages.
-
#
-
# The messages have associated levels, such as +INFO+ or +ERROR+ that indicate
-
# their importance. You can then give the Logger a level, and only messages
-
# at that level of higher will be printed.
-
#
-
# The levels are:
-
#
-
# +FATAL+:: an unhandleable error that results in a program crash
-
# +ERROR+:: a handleable error condition
-
# +WARN+:: a warning
-
# +INFO+:: generic (useful) information about system operation
-
# +DEBUG+:: low-level information for developers
-
#
-
# For instance, in a production system, you may have your Logger set to
-
# +INFO+ or even +WARN+
-
# When you are developing the system, however, you probably
-
# want to know about the program's internal state, and would set the Logger to
-
# +DEBUG+.
-
#
-
# *Note*: Logger does not escape or sanitize any messages passed to it.
-
# Developers should be aware of when potentially malicious data (user-input)
-
# is passed to Logger, and manually escape the untrusted data:
-
#
-
# logger.info("User-input: #{input.dump}")
-
# logger.info("User-input: %p" % input)
-
#
-
# You can use #formatter= for escaping all data.
-
#
-
# original_formatter = Logger::Formatter.new
-
# logger.formatter = proc { |severity, datetime, progname, msg|
-
# original_formatter.call(severity, datetime, progname, msg.dump)
-
# }
-
# logger.info(input)
-
#
-
# === Example
-
#
-
# This creates a logger to the standard output stream, with a level of +WARN+
-
#
-
# log = Logger.new(STDOUT)
-
# log.level = Logger::WARN
-
#
-
# log.debug("Created logger")
-
# log.info("Program started")
-
# log.warn("Nothing to do!")
-
#
-
# begin
-
# File.each_line(path) do |line|
-
# unless line =~ /^(\w+) = (.*)$/
-
# log.error("Line in wrong format: #{line}")
-
# end
-
# end
-
# rescue => err
-
# log.fatal("Caught exception; exiting")
-
# log.fatal(err)
-
# end
-
#
-
# Because the Logger's level is set to +WARN+, only the warning, error, and
-
# fatal messages are recorded. The debug and info messages are silently
-
# discarded.
-
#
-
# === Features
-
#
-
# There are several interesting features that Logger provides, like
-
# auto-rolling of log files, setting the format of log messages, and
-
# specifying a program name in conjunction with the message. The next section
-
# shows you how to achieve these things.
-
#
-
#
-
# == HOWTOs
-
#
-
# === How to create a logger
-
#
-
# The options below give you various choices, in more or less increasing
-
# complexity.
-
#
-
# 1. Create a logger which logs messages to STDERR/STDOUT.
-
#
-
# logger = Logger.new(STDERR)
-
# logger = Logger.new(STDOUT)
-
#
-
# 2. Create a logger for the file which has the specified name.
-
#
-
# logger = Logger.new('logfile.log')
-
#
-
# 3. Create a logger for the specified file.
-
#
-
# file = File.open('foo.log', File::WRONLY | File::APPEND)
-
# # To create new (and to remove old) logfile, add File::CREAT like;
-
# # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
-
# logger = Logger.new(file)
-
#
-
# 4. Create a logger which ages logfile once it reaches a certain size. Leave
-
# 10 "old log files" and each file is about 1,024,000 bytes.
-
#
-
# logger = Logger.new('foo.log', 10, 1024000)
-
#
-
# 5. Create a logger which ages logfile daily/weekly/monthly.
-
#
-
# logger = Logger.new('foo.log', 'daily')
-
# logger = Logger.new('foo.log', 'weekly')
-
# logger = Logger.new('foo.log', 'monthly')
-
#
-
# === How to log a message
-
#
-
# Notice the different methods (+fatal+, +error+, +info+) being used to log
-
# messages of various levels? Other methods in this family are +warn+ and
-
# +debug+. +add+ is used below to log a message of an arbitrary (perhaps
-
# dynamic) level.
-
#
-
# 1. Message in block.
-
#
-
# logger.fatal { "Argument 'foo' not given." }
-
#
-
# 2. Message as a string.
-
#
-
# logger.error "Argument #{ @foo } mismatch."
-
#
-
# 3. With progname.
-
#
-
# logger.info('initialize') { "Initializing..." }
-
#
-
# 4. With severity.
-
#
-
# logger.add(Logger::FATAL) { 'Fatal error!' }
-
#
-
# The block form allows you to create potentially complex log messages,
-
# but to delay their evaluation until and unless the message is
-
# logged. For example, if we have the following:
-
#
-
# logger.debug { "This is a " + potentially + " expensive operation" }
-
#
-
# If the logger's level is +INFO+ or higher, no debug messages will be logged,
-
# and the entire block will not even be evaluated. Compare to this:
-
#
-
# logger.debug("This is a " + potentially + " expensive operation")
-
#
-
# Here, the string concatenation is done every time, even if the log
-
# level is not set to show the debug message.
-
#
-
# === How to close a logger
-
#
-
# logger.close
-
#
-
# === Setting severity threshold
-
#
-
# 1. Original interface.
-
#
-
# logger.sev_threshold = Logger::WARN
-
#
-
# 2. Log4r (somewhat) compatible interface.
-
#
-
# logger.level = Logger::INFO
-
#
-
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
-
#
-
#
-
# == Format
-
#
-
# Log messages are rendered in the output stream in a certain format by
-
# default. The default format and a sample are shown below:
-
#
-
# Log format:
-
# SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message
-
#
-
# Log sample:
-
# I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info.
-
#
-
# You may change the date and time format via #datetime_format=
-
#
-
# logger.datetime_format = "%Y-%m-%d %H:%M:%S"
-
# # e.g. "2004-01-03 00:54:26"
-
#
-
# Or, you may change the overall format with #formatter= method.
-
#
-
# logger.formatter = proc do |severity, datetime, progname, msg|
-
# "#{datetime}: #{msg}\n"
-
# end
-
# # e.g. "Thu Sep 22 08:51:08 GMT+9:00 2005: hello world"
-
#
-
1
class Logger
-
1
VERSION = "1.2.7"
-
1
_, name, rev = %w$Id: logger.rb 31641 2011-05-19 00:07:25Z nobu $
-
1
if name
-
1
name = name.chomp(",v")
-
else
-
name = File.basename(__FILE__)
-
end
-
1
rev ||= "v#{VERSION}"
-
1
ProgName = "#{name}/#{rev}"
-
-
1
class Error < RuntimeError # :nodoc:
-
end
-
# not used after 1.2.7. just for compat.
-
1
class ShiftingError < Error # :nodoc:
-
end
-
-
# Logging severity.
-
1
module Severity
-
# Low-level information, mostly for developers
-
1
DEBUG = 0
-
# generic, useful information about system operation
-
1
INFO = 1
-
# a warning
-
1
WARN = 2
-
# a handleable error condition
-
1
ERROR = 3
-
# an unhandleable error that results in a program crash
-
1
FATAL = 4
-
# an unknown message that should always be logged
-
1
UNKNOWN = 5
-
end
-
1
include Severity
-
-
# Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
-
1
attr_accessor :level
-
-
# program name to include in log messages.
-
1
attr_accessor :progname
-
-
# Set date-time format.
-
#
-
# +datetime_format+:: A string suitable for passing to +strftime+.
-
1
def datetime_format=(datetime_format)
-
@default_formatter.datetime_format = datetime_format
-
end
-
-
# Returns the date format being used. See #datetime_format=
-
1
def datetime_format
-
@default_formatter.datetime_format
-
end
-
-
# Logging formatter, as a +Proc+ that will take four arguments and
-
# return the formatted message. The arguments are:
-
#
-
# +severity+:: The Severity of the log message
-
# +time+:: A Time instance representing when the message was logged
-
# +progname+:: The #progname configured, or passed to the logger method
-
# +msg+:: The _Object_ the user passed to the log message; not necessarily a
-
# String.
-
#
-
# The block should return an Object that can be written to the logging
-
# device via +write+. The default formatter is used when no formatter is
-
# set.
-
1
attr_accessor :formatter
-
-
1
alias sev_threshold level
-
1
alias sev_threshold= level=
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +DEBUG+ messages.
-
1
def debug?; @level <= DEBUG; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +INFO+ messages.
-
1
def info?; @level <= INFO; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +WARN+ messages.
-
1
def warn?; @level <= WARN; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +ERROR+ messages.
-
1
def error?; @level <= ERROR; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +FATAL+ messages.
-
1
def fatal?; @level <= FATAL; end
-
-
#
-
# === Synopsis
-
#
-
# Logger.new(name, shift_age = 7, shift_size = 1048576)
-
# Logger.new(name, shift_age = 'weekly')
-
#
-
# === Args
-
#
-
# +logdev+::
-
# The log device. This is a filename (String) or IO object (typically
-
# +STDOUT+, +STDERR+, or an open file).
-
# +shift_age+::
-
# Number of old log files to keep, *or* frequency of rotation (+daily+,
-
# +weekly+ or +monthly+).
-
# +shift_size+::
-
# Maximum logfile size (only applies when +shift_age+ is a number).
-
#
-
# === Description
-
#
-
# Create an instance.
-
#
-
1
def initialize(logdev, shift_age = 0, shift_size = 1048576)
-
@progname = nil
-
@level = DEBUG
-
@default_formatter = Formatter.new
-
@formatter = nil
-
@logdev = nil
-
if logdev
-
@logdev = LogDevice.new(logdev, :shift_age => shift_age,
-
:shift_size => shift_size)
-
end
-
end
-
-
#
-
# === Synopsis
-
#
-
# Logger#add(severity, message = nil, progname = nil) { ... }
-
#
-
# === Args
-
#
-
# +severity+::
-
# Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
-
# +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
-
# +message+::
-
# The log message. A String or Exception.
-
# +progname+::
-
# Program name string. Can be omitted. Treated as a message if no
-
# +message+ and +block+ are given.
-
# +block+::
-
# Can be omitted. Called to get a message string if +message+ is nil.
-
#
-
# === Return
-
#
-
# +true+ if successful, +false+ otherwise.
-
#
-
# When the given severity is not high enough (for this particular logger), log
-
# no message, and return +true+.
-
#
-
# === Description
-
#
-
# Log a message if the given severity is high enough. This is the generic
-
# logging method. Users will be more inclined to use #debug, #info, #warn,
-
# #error, and #fatal.
-
#
-
# <b>Message format</b>: +message+ can be any object, but it has to be
-
# converted to a String in order to log it. Generally, +inspect+ is used
-
# if the given object is not a String.
-
# A special case is an +Exception+ object, which will be printed in detail,
-
# including message, class, and backtrace. See #msg2str for the
-
# implementation if required.
-
#
-
# === Bugs
-
#
-
# * Logfile is not locked.
-
# * Append open does not need to lock file.
-
# * If the OS which supports multi I/O, records possibly be mixed.
-
#
-
1
def add(severity, message = nil, progname = nil, &block)
-
severity ||= UNKNOWN
-
if @logdev.nil? or severity < @level
-
return true
-
end
-
progname ||= @progname
-
if message.nil?
-
if block_given?
-
message = yield
-
else
-
message = progname
-
progname = @progname
-
end
-
end
-
@logdev.write(
-
format_message(format_severity(severity), Time.now, progname, message))
-
true
-
end
-
1
alias log add
-
-
#
-
# Dump given message to the log device without any formatting. If no log
-
# device exists, return +nil+.
-
#
-
1
def <<(msg)
-
unless @logdev.nil?
-
@logdev.write(msg)
-
end
-
end
-
-
#
-
# Log a +DEBUG+ message.
-
#
-
# See #info for more information.
-
#
-
1
def debug(progname = nil, &block)
-
add(DEBUG, nil, progname, &block)
-
end
-
-
#
-
# :call-seq:
-
# info(message)
-
# info(progname,&block)
-
#
-
# Log an +INFO+ message.
-
#
-
# +message+:: the message to log; does not need to be a String
-
# +progname+:: in the block form, this is the #progname to use in the
-
# the log message. The default can be set with #progname=
-
# <tt>&block</tt>:: evaluates to the message to log. This is not evaluated
-
# unless the logger's level is sufficient
-
# to log the message. This allows you to create
-
# potentially expensive logging messages that are
-
# only called when the logger is configured to show them.
-
#
-
# === Examples
-
#
-
# logger.info("MainApp") { "Received connection from #{ip}" }
-
# # ...
-
# logger.info "Waiting for input from user"
-
# # ...
-
# logger.info { "User typed #{input}" }
-
#
-
# You'll probably stick to the second form above, unless you want to provide a
-
# program name (which you can do with #progname= as well).
-
#
-
# === Return
-
#
-
# See #add.
-
#
-
1
def info(progname = nil, &block)
-
add(INFO, nil, progname, &block)
-
end
-
-
#
-
# Log a +WARN+ message.
-
#
-
# See #info for more information.
-
#
-
1
def warn(progname = nil, &block)
-
add(WARN, nil, progname, &block)
-
end
-
-
#
-
# Log an +ERROR+ message.
-
#
-
# See #info for more information.
-
#
-
1
def error(progname = nil, &block)
-
add(ERROR, nil, progname, &block)
-
end
-
-
#
-
# Log a +FATAL+ message.
-
#
-
# See #info for more information.
-
#
-
1
def fatal(progname = nil, &block)
-
add(FATAL, nil, progname, &block)
-
end
-
-
#
-
# Log an +UNKNOWN+ message. This will be printed no matter what the logger's
-
# level.
-
#
-
# See #info for more information.
-
#
-
1
def unknown(progname = nil, &block)
-
add(UNKNOWN, nil, progname, &block)
-
end
-
-
#
-
# Close the logging device.
-
#
-
1
def close
-
@logdev.close if @logdev
-
end
-
-
1
private
-
-
# Severity label for logging. (max 5 char)
-
1
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY)
-
-
1
def format_severity(severity)
-
SEV_LABEL[severity] || 'ANY'
-
end
-
-
1
def format_message(severity, datetime, progname, msg)
-
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
-
end
-
-
-
# Default formatter for log messages
-
1
class Formatter
-
1
Format = "%s, [%s#%d] %5s -- %s: %s\n"
-
-
1
attr_accessor :datetime_format
-
-
1
def initialize
-
@datetime_format = nil
-
end
-
-
1
def call(severity, time, progname, msg)
-
Format % [severity[0..0], format_datetime(time), $$, severity, progname,
-
msg2str(msg)]
-
end
-
-
1
private
-
-
1
def format_datetime(time)
-
if @datetime_format.nil?
-
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
-
else
-
time.strftime(@datetime_format)
-
end
-
end
-
-
1
def msg2str(msg)
-
case msg
-
when ::String
-
msg
-
when ::Exception
-
"#{ msg.message } (#{ msg.class })\n" <<
-
(msg.backtrace || []).join("\n")
-
else
-
msg.inspect
-
end
-
end
-
end
-
-
-
# Device used for logging messages.
-
1
class LogDevice
-
1
attr_reader :dev
-
1
attr_reader :filename
-
-
1
class LogDeviceMutex
-
1
include MonitorMixin
-
end
-
-
1
def initialize(log = nil, opt = {})
-
@dev = @filename = @shift_age = @shift_size = nil
-
@mutex = LogDeviceMutex.new
-
if log.respond_to?(:write) and log.respond_to?(:close)
-
@dev = log
-
else
-
@dev = open_logfile(log)
-
@dev.sync = true
-
@filename = log
-
@shift_age = opt[:shift_age] || 7
-
@shift_size = opt[:shift_size] || 1048576
-
end
-
end
-
-
1
def write(message)
-
begin
-
@mutex.synchronize do
-
if @shift_age and @dev.respond_to?(:stat)
-
begin
-
check_shift_log
-
rescue
-
warn("log shifting failed. #{$!}")
-
end
-
end
-
begin
-
@dev.write(message)
-
rescue
-
warn("log writing failed. #{$!}")
-
end
-
end
-
rescue Exception => ignored
-
warn("log writing failed. #{ignored}")
-
end
-
end
-
-
1
def close
-
begin
-
@mutex.synchronize do
-
@dev.close rescue nil
-
end
-
rescue Exception
-
@dev.close rescue nil
-
end
-
end
-
-
1
private
-
-
1
def open_logfile(filename)
-
if (FileTest.exist?(filename))
-
open(filename, (File::WRONLY | File::APPEND))
-
else
-
create_logfile(filename)
-
end
-
end
-
-
1
def create_logfile(filename)
-
logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
-
logdev.sync = true
-
add_log_header(logdev)
-
logdev
-
end
-
-
1
def add_log_header(file)
-
file.write(
-
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
-
)
-
end
-
-
1
SiD = 24 * 60 * 60
-
-
1
def check_shift_log
-
if @shift_age.is_a?(Integer)
-
# Note: always returns false if '0'.
-
if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
-
shift_log_age
-
end
-
else
-
now = Time.now
-
period_end = previous_period_end(now)
-
if @dev.stat.mtime <= period_end
-
shift_log_period(period_end)
-
end
-
end
-
end
-
-
1
def shift_log_age
-
(@shift_age-3).downto(0) do |i|
-
if FileTest.exist?("#{@filename}.#{i}")
-
File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
-
end
-
end
-
@dev.close rescue nil
-
File.rename("#{@filename}", "#{@filename}.0")
-
@dev = create_logfile(@filename)
-
return true
-
end
-
-
1
def shift_log_period(period_end)
-
postfix = period_end.strftime("%Y%m%d") # YYYYMMDD
-
age_file = "#{@filename}.#{postfix}"
-
if FileTest.exist?(age_file)
-
# try to avoid filename crash caused by Timestamp change.
-
idx = 0
-
# .99 can be overridden; avoid too much file search with 'loop do'
-
while idx < 100
-
idx += 1
-
age_file = "#{@filename}.#{postfix}.#{idx}"
-
break unless FileTest.exist?(age_file)
-
end
-
end
-
@dev.close rescue nil
-
File.rename("#{@filename}", age_file)
-
@dev = create_logfile(@filename)
-
return true
-
end
-
-
1
def previous_period_end(now)
-
case @shift_age
-
when /^daily$/
-
eod(now - 1 * SiD)
-
when /^weekly$/
-
eod(now - ((now.wday + 1) * SiD))
-
when /^monthly$/
-
eod(now - now.mday * SiD)
-
else
-
now
-
end
-
end
-
-
1
def eod(t)
-
Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
-
end
-
end
-
-
-
#
-
# == Description
-
#
-
# Application -- Add logging support to your application.
-
#
-
# == Usage
-
#
-
# 1. Define your application class as a sub-class of this class.
-
# 2. Override 'run' method in your class to do many things.
-
# 3. Instantiate it and invoke 'start'.
-
#
-
# == Example
-
#
-
# class FooApp < Application
-
# def initialize(foo_app, application_specific, arguments)
-
# super('FooApp') # Name of the application.
-
# end
-
#
-
# def run
-
# ...
-
# log(WARN, 'warning', 'my_method1')
-
# ...
-
# @log.error('my_method2') { 'Error!' }
-
# ...
-
# end
-
# end
-
#
-
# status = FooApp.new(....).start
-
#
-
1
class Application
-
1
include Logger::Severity
-
-
# Name of the application given at initialize.
-
1
attr_reader :appname
-
-
#
-
# == Synopsis
-
#
-
# Application.new(appname = '')
-
#
-
# == Args
-
#
-
# +appname+:: Name of the application.
-
#
-
# == Description
-
#
-
# Create an instance. Log device is +STDERR+ by default. This can be
-
# changed with #set_log.
-
#
-
1
def initialize(appname = nil)
-
@appname = appname
-
@log = Logger.new(STDERR)
-
@log.progname = @appname
-
@level = @log.level
-
end
-
-
#
-
# Start the application. Return the status code.
-
#
-
1
def start
-
status = -1
-
begin
-
log(INFO, "Start of #{ @appname }.")
-
status = run
-
rescue
-
log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n"))
-
ensure
-
log(INFO, "End of #{ @appname }. (status: #{ status.to_s })")
-
end
-
status
-
end
-
-
# Logger for this application. See the class Logger for an explanation.
-
1
def logger
-
@log
-
end
-
-
#
-
# Sets the logger for this application. See the class Logger for an
-
# explanation.
-
#
-
1
def logger=(logger)
-
@log = logger
-
@log.progname = @appname
-
@log.level = @level
-
end
-
-
#
-
# Sets the log device for this application. See <tt>Logger.new</tt> for
-
# an explanation of the arguments.
-
#
-
1
def set_log(logdev, shift_age = 0, shift_size = 1024000)
-
@log = Logger.new(logdev, shift_age, shift_size)
-
@log.progname = @appname
-
@log.level = @level
-
end
-
-
1
def log=(logdev)
-
set_log(logdev)
-
end
-
-
#
-
# Set the logging threshold, just like <tt>Logger#level=</tt>.
-
#
-
1
def level=(level)
-
@level = level
-
@log.level = @level
-
end
-
-
#
-
# See Logger#add. This application's +appname+ is used.
-
#
-
1
def log(severity, message = nil, &block)
-
@log.add(severity, message, @appname, &block) if @log
-
end
-
-
1
private
-
-
1
def run
-
# TODO: should be an NotImplementedError
-
raise RuntimeError.new('Method run must be defined in the derived class.')
-
end
-
end
-
end
-
#
-
# mutex_m.rb -
-
# $Release Version: 3.0$
-
# $Revision: 1.7 $
-
# Original from mutex.rb
-
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
-
# modified by matz
-
# patched by akira yamada
-
#
-
# --
-
# Usage:
-
# require "mutex_m.rb"
-
# obj = Object.new
-
# obj.extend Mutex_m
-
# ...
-
# extended object can be handled like Mutex
-
# or
-
# class Foo
-
# include Mutex_m
-
# ...
-
# end
-
# obj = Foo.new
-
# this obj can be handled like Mutex
-
#
-
-
1
require 'thread'
-
-
1
module Mutex_m
-
1
def Mutex_m.define_aliases(cl)
-
1
cl.module_eval %q{
-
alias locked? mu_locked?
-
alias lock mu_lock
-
alias unlock mu_unlock
-
alias try_lock mu_try_lock
-
alias synchronize mu_synchronize
-
}
-
end
-
-
1
def Mutex_m.append_features(cl)
-
1
super
-
1
define_aliases(cl) unless cl.instance_of?(Module)
-
end
-
-
1
def Mutex_m.extend_object(obj)
-
super
-
obj.mu_extended
-
end
-
-
1
def mu_extended
-
unless (defined? locked? and
-
defined? lock and
-
defined? unlock and
-
defined? try_lock and
-
defined? synchronize)
-
Mutex_m.define_aliases(singleton_class)
-
end
-
mu_initialize
-
end
-
-
# locking
-
1
def mu_synchronize(&block)
-
@_mutex.synchronize(&block)
-
end
-
-
1
def mu_locked?
-
@_mutex.locked?
-
end
-
-
1
def mu_try_lock
-
@_mutex.try_lock
-
end
-
-
1
def mu_lock
-
@_mutex.lock
-
end
-
-
1
def mu_unlock
-
@_mutex.unlock
-
end
-
-
1
def sleep(timeout = nil)
-
@_mutex.sleep(timeout)
-
end
-
-
1
private
-
-
1
def mu_initialize
-
1
@_mutex = Mutex.new
-
end
-
-
1
def initialize(*args)
-
1
mu_initialize
-
1
super
-
end
-
end
-
=begin
-
= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: openssl.rb 32665 2011-07-25 06:38:44Z nahi $
-
=end
-
-
1
require 'openssl.so'
-
-
1
require 'openssl/bn'
-
1
require 'openssl/cipher'
-
1
require 'openssl/config'
-
1
require 'openssl/digest'
-
1
require 'openssl/ssl-internal'
-
1
require 'openssl/x509-internal'
-
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space definitions that completes C-space funcs for BN
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: bn.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class BN
-
1
include Comparable
-
end # BN
-
end # OpenSSL
-
-
##
-
# Add double dispatch to Integer
-
#
-
1
class Integer
-
1
def to_bn
-
OpenSSL::BN::new(self.to_s(16), 16)
-
end
-
end # Integer
-
-
=begin
-
= $RCSfile$ -- Buffering mix-in module.
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: buffering.rb 32012 2011-06-11 14:07:42Z nahi $
-
=end
-
-
##
-
# OpenSSL IO buffering mix-in module.
-
#
-
# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-
-
1
module OpenSSL::Buffering
-
1
include Enumerable
-
-
##
-
# The "sync mode" of the SSLSocket.
-
#
-
# See IO#sync for full details.
-
-
1
attr_accessor :sync
-
-
##
-
# Default size to read from or write to the SSLSocket for buffer operations.
-
-
1
BLOCK_SIZE = 1024*16
-
-
1
def initialize(*args)
-
@eof = false
-
@rbuffer = ""
-
@sync = @io.sync
-
end
-
-
#
-
# for reading.
-
#
-
1
private
-
-
##
-
# Fills the buffer from the underlying SSLSocket
-
-
1
def fill_rbuff
-
begin
-
@rbuffer << self.sysread(BLOCK_SIZE)
-
rescue Errno::EAGAIN
-
retry
-
rescue EOFError
-
@eof = true
-
end
-
end
-
-
##
-
# Consumes +size+ bytes from the buffer
-
-
1
def consume_rbuff(size=nil)
-
if @rbuffer.empty?
-
nil
-
else
-
size = @rbuffer.size unless size
-
ret = @rbuffer[0, size]
-
@rbuffer[0, size] = ""
-
ret
-
end
-
end
-
-
1
public
-
-
##
-
# Reads +size+ bytes from the stream. If +buf+ is provided it must
-
# reference a string which will receive the data.
-
#
-
# See IO#read for full details.
-
-
1
def read(size=nil, buf=nil)
-
if size == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
until @eof
-
break if size && size <= @rbuffer.size
-
fill_rbuff
-
end
-
ret = consume_rbuff(size) || ""
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
(size && ret.empty?) ? nil : ret
-
end
-
-
##
-
# Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it
-
# must reference a string which will receive the data.
-
#
-
# See IO#readpartial for full details.
-
-
1
def readpartial(maxlen, buf=nil)
-
if maxlen == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
if @rbuffer.empty?
-
begin
-
return sysread(maxlen, buf)
-
rescue Errno::EAGAIN
-
retry
-
end
-
end
-
ret = consume_rbuff(maxlen)
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
raise EOFError if ret.empty?
-
ret
-
end
-
-
##
-
# Reads at most +maxlen+ bytes in the non-blocking manner.
-
#
-
# When no data can be read without blocking it raises
-
# OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-
#
-
# IO::WaitReadable means SSL needs to read internally so read_nonblock
-
# should be called again when the underlying IO is readable.
-
#
-
# IO::WaitWritable means SSL needs to write internally so read_nonblock
-
# should be called again after the underlying IO is writable.
-
#
-
# OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
-
#
-
# # emulates blocking read (readpartial).
-
# begin
-
# result = ssl.read_nonblock(maxlen)
-
# rescue IO::WaitReadable
-
# IO.select([io])
-
# retry
-
# rescue IO::WaitWritable
-
# IO.select(nil, [io])
-
# retry
-
# end
-
#
-
# Note that one reason that read_nonblock writes to the underlying IO is
-
# when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
-
# more details. http://www.openssl.org/support/faq.html
-
-
1
def read_nonblock(maxlen, buf=nil)
-
if maxlen == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
if @rbuffer.empty?
-
return sysread_nonblock(maxlen, buf)
-
end
-
ret = consume_rbuff(maxlen)
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
raise EOFError if ret.empty?
-
ret
-
end
-
-
##
-
# Reads the next "line+ from the stream. Lines are separated by +eol+. If
-
# +limit+ is provided the result will not be longer than the given number of
-
# bytes.
-
#
-
# +eol+ may be a String or Regexp.
-
#
-
# Unlike IO#gets the line read will not be assigned to +$_+.
-
#
-
# Unlike IO#gets the separator must be provided if a limit is provided.
-
-
1
def gets(eol=$/, limit=nil)
-
idx = @rbuffer.index(eol)
-
until @eof
-
break if idx
-
fill_rbuff
-
idx = @rbuffer.index(eol)
-
end
-
if eol.is_a?(Regexp)
-
size = idx ? idx+$&.size : nil
-
else
-
size = idx ? idx+eol.size : nil
-
end
-
if limit and limit >= 0
-
size = [size, limit].min
-
end
-
consume_rbuff(size)
-
end
-
-
##
-
# Executes the block for every line in the stream where lines are separated
-
# by +eol+.
-
#
-
# See also #gets
-
-
1
def each(eol=$/)
-
while line = self.gets(eol)
-
yield line
-
end
-
end
-
1
alias each_line each
-
-
##
-
# Reads lines from the stream which are separated by +eol+.
-
#
-
# See also #gets
-
-
1
def readlines(eol=$/)
-
ary = []
-
while line = self.gets(eol)
-
ary << line
-
end
-
ary
-
end
-
-
##
-
# Reads a line from the stream which is separated by +eol+.
-
#
-
# Raises EOFError if at end of file.
-
-
1
def readline(eol=$/)
-
raise EOFError if eof?
-
gets(eol)
-
end
-
-
##
-
# Reads one character from the stream. Returns nil if called at end of
-
# file.
-
-
1
def getc
-
read(1)
-
end
-
-
##
-
# Calls the given block once for each byte in the stream.
-
-
1
def each_byte # :yields: byte
-
while c = getc
-
yield(c.ord)
-
end
-
end
-
-
##
-
# Reads a one-character string from the stream. Raises an EOFError at end
-
# of file.
-
-
1
def readchar
-
raise EOFError if eof?
-
getc
-
end
-
-
##
-
# Pushes character +c+ back onto the stream such that a subsequent buffered
-
# character read will return it.
-
#
-
# Unlike IO#getc multiple bytes may be pushed back onto the stream.
-
#
-
# Has no effect on unbuffered reads (such as #sysread).
-
-
1
def ungetc(c)
-
@rbuffer[0,0] = c.chr
-
end
-
-
##
-
# Returns true if the stream is at file which means there is no more data to
-
# be read.
-
-
1
def eof?
-
fill_rbuff if !@eof && @rbuffer.empty?
-
@eof && @rbuffer.empty?
-
end
-
1
alias eof eof?
-
-
#
-
# for writing.
-
#
-
1
private
-
-
##
-
# Writes +s+ to the buffer. When the buffer is full or #sync is true the
-
# buffer is flushed to the underlying socket.
-
-
1
def do_write(s)
-
@wbuffer = "" unless defined? @wbuffer
-
@wbuffer << s
-
@sync ||= false
-
if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
-
remain = idx ? idx + $/.size : @wbuffer.length
-
nwritten = 0
-
while remain > 0
-
str = @wbuffer[nwritten,remain]
-
begin
-
nwrote = syswrite(str)
-
rescue Errno::EAGAIN
-
retry
-
end
-
remain -= nwrote
-
nwritten += nwrote
-
end
-
@wbuffer[0,nwritten] = ""
-
end
-
end
-
-
1
public
-
-
##
-
# Writes +s+ to the stream. If the argument is not a string it will be
-
# converted using String#to_s. Returns the number of bytes written.
-
-
1
def write(s)
-
do_write(s)
-
s.length
-
end
-
-
##
-
# Writes +str+ in the non-blocking manner.
-
#
-
# If there is buffered data, it is flushed first. This may block.
-
#
-
# write_nonblock returns number of bytes written to the SSL connection.
-
#
-
# When no data can be written without blocking it raises
-
# OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-
#
-
# IO::WaitReadable means SSL needs to read internally so write_nonblock
-
# should be called again after the underlying IO is readable.
-
#
-
# IO::WaitWritable means SSL needs to write internally so write_nonblock
-
# should be called again after underlying IO is writable.
-
#
-
# So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
-
#
-
# # emulates blocking write.
-
# begin
-
# result = ssl.write_nonblock(str)
-
# rescue IO::WaitReadable
-
# IO.select([io])
-
# retry
-
# rescue IO::WaitWritable
-
# IO.select(nil, [io])
-
# retry
-
# end
-
#
-
# Note that one reason that write_nonblock reads from the underlying IO
-
# is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
-
# for more details. http://www.openssl.org/support/faq.html
-
-
1
def write_nonblock(s)
-
flush
-
syswrite_nonblock(s)
-
end
-
-
##
-
# Writes +s+ to the stream. +s+ will be converted to a String using
-
# String#to_s.
-
-
1
def << (s)
-
do_write(s)
-
self
-
end
-
-
##
-
# Writes +args+ to the stream along with a record separator.
-
#
-
# See IO#puts for full details.
-
-
1
def puts(*args)
-
s = ""
-
if args.empty?
-
s << "\n"
-
end
-
args.each{|arg|
-
s << arg.to_s
-
if $/ && /\n\z/ !~ s
-
s << "\n"
-
end
-
}
-
do_write(s)
-
nil
-
end
-
-
##
-
# Writes +args+ to the stream.
-
#
-
# See IO#print for full details.
-
-
1
def print(*args)
-
s = ""
-
args.each{ |arg| s << arg.to_s }
-
do_write(s)
-
nil
-
end
-
-
##
-
# Formats and writes to the stream converting parameters under control of
-
# the format string.
-
#
-
# See Kernel#sprintf for format string details.
-
-
1
def printf(s, *args)
-
do_write(s % args)
-
nil
-
end
-
-
##
-
# Flushes buffered data to the SSLSocket.
-
-
1
def flush
-
osync = @sync
-
@sync = true
-
do_write ""
-
return self
-
ensure
-
@sync = osync
-
end
-
-
##
-
# Closes the SSLSocket and flushes any unwritten data.
-
-
1
def close
-
flush rescue nil
-
sysclose
-
end
-
end
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space predefined Cipher subclasses
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: cipher.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class Cipher
-
1
%w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
-
8
klass = Class.new(Cipher){
-
8
define_method(:initialize){|*args|
-
cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
-
super(cipher_name)
-
}
-
}
-
8
const_set(name, klass)
-
}
-
-
1
%w(128 192 256).each{|keylen|
-
3
klass = Class.new(Cipher){
-
3
define_method(:initialize){|mode|
-
mode ||= "CBC"
-
cipher_name = "AES-#{keylen}-#{mode}"
-
super(cipher_name)
-
}
-
}
-
3
const_set("AES#{keylen}", klass)
-
}
-
-
# Generate, set, and return a random key.
-
# You must call cipher.encrypt or cipher.decrypt before calling this method.
-
1
def random_key
-
str = OpenSSL::Random.random_bytes(self.key_len)
-
self.key = str
-
return str
-
end
-
-
# Generate, set, and return a random iv.
-
# You must call cipher.encrypt or cipher.decrypt before calling this method.
-
1
def random_iv
-
str = OpenSSL::Random.random_bytes(self.iv_len)
-
self.iv = str
-
return str
-
end
-
-
# This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future.
-
1
class Cipher < Cipher
-
# add warning
-
end
-
end # Cipher
-
end # OpenSSL
-
=begin
-
= Ruby-space definitions that completes C-space funcs for Config
-
-
= Info
-
Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
=end
-
-
1
require 'stringio'
-
-
1
module OpenSSL
-
1
class Config
-
1
include Enumerable
-
-
1
class << self
-
1
def parse(str)
-
c = new()
-
parse_config(StringIO.new(str)).each do |section, hash|
-
c[section] = hash
-
end
-
c
-
end
-
-
1
alias load new
-
-
1
def parse_config(io)
-
begin
-
parse_config_lines(io)
-
rescue ConfigError => e
-
e.message.replace("error in line #{io.lineno}: " + e.message)
-
raise
-
end
-
end
-
-
1
def get_key_string(data, section, key) # :nodoc:
-
if v = data[section] && data[section][key]
-
return v
-
elsif section == 'ENV'
-
if v = ENV[key]
-
return v
-
end
-
end
-
if v = data['default'] && data['default'][key]
-
return v
-
end
-
end
-
-
1
private
-
-
1
def parse_config_lines(io)
-
section = 'default'
-
data = {section => {}}
-
while definition = get_definition(io)
-
definition = clear_comments(definition)
-
next if definition.empty?
-
if definition[0] == ?[
-
if /\[([^\]]*)\]/ =~ definition
-
section = $1.strip
-
data[section] ||= {}
-
else
-
raise ConfigError, "missing close square bracket"
-
end
-
else
-
if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
-
if $2
-
section = $1
-
key = $2
-
else
-
key = $1
-
end
-
value = unescape_value(data, section, $3)
-
(data[section] ||= {})[key] = value.strip
-
else
-
raise ConfigError, "missing equal sign"
-
end
-
end
-
end
-
data
-
end
-
-
# escape with backslash
-
1
QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
-
# escape with backslash and doubled dq
-
1
QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
-
# escaped char map
-
1
ESCAPE_MAP = {
-
"r" => "\r",
-
"n" => "\n",
-
"b" => "\b",
-
"t" => "\t",
-
}
-
-
1
def unescape_value(data, section, value)
-
scanned = []
-
while m = value.match(/['"\\$]/)
-
scanned << m.pre_match
-
c = m[0]
-
value = m.post_match
-
case c
-
when "'"
-
if m = value.match(QUOTE_REGEXP_SQ)
-
scanned << m[1].gsub(/\\(.)/, '\\1')
-
value = m.post_match
-
else
-
break
-
end
-
when '"'
-
if m = value.match(QUOTE_REGEXP_DQ)
-
scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
-
value = m.post_match
-
else
-
break
-
end
-
when "\\"
-
c = value.slice!(0, 1)
-
scanned << (ESCAPE_MAP[c] || c)
-
when "$"
-
ref, value = extract_reference(value)
-
refsec = section
-
if ref.index('::')
-
refsec, ref = ref.split('::', 2)
-
end
-
if v = get_key_string(data, refsec, ref)
-
scanned << v
-
else
-
raise ConfigError, "variable has no value"
-
end
-
else
-
raise 'must not reaced'
-
end
-
end
-
scanned << value
-
scanned.join
-
end
-
-
1
def extract_reference(value)
-
rest = ''
-
if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
-
value = m[1] || m[2]
-
rest = m.post_match
-
elsif [?(, ?{].include?(value[0])
-
raise ConfigError, "no close brace"
-
end
-
if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
-
return m[0], m.post_match + rest
-
else
-
raise
-
end
-
end
-
-
1
def clear_comments(line)
-
# FCOMMENT
-
if m = line.match(/\A([\t\n\f ]*);.*\z/)
-
return m[1]
-
end
-
# COMMENT
-
scanned = []
-
while m = line.match(/[#'"\\]/)
-
scanned << m.pre_match
-
c = m[0]
-
line = m.post_match
-
case c
-
when '#'
-
line = nil
-
break
-
when "'", '"'
-
regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
-
scanned << c
-
if m = line.match(regexp)
-
scanned << m[0]
-
line = m.post_match
-
else
-
scanned << line
-
line = nil
-
break
-
end
-
when "\\"
-
scanned << c
-
scanned << line.slice!(0, 1)
-
else
-
raise 'must not reaced'
-
end
-
end
-
scanned << line
-
scanned.join
-
end
-
-
1
def get_definition(io)
-
if line = get_line(io)
-
while /[^\\]\\\z/ =~ line
-
if extra = get_line(io)
-
line += extra
-
else
-
break
-
end
-
end
-
return line.strip
-
end
-
end
-
-
1
def get_line(io)
-
if line = io.gets
-
line.gsub(/[\r\n]*/, '')
-
end
-
end
-
end
-
-
1
def initialize(filename = nil)
-
@data = {}
-
if filename
-
File.open(filename.to_s) do |file|
-
Config.parse_config(file).each do |section, hash|
-
self[section] = hash
-
end
-
end
-
end
-
end
-
-
1
def get_value(section, key)
-
if section.nil?
-
raise TypeError.new('nil not allowed')
-
end
-
section = 'default' if section.empty?
-
get_key_string(section, key)
-
end
-
-
1
def value(arg1, arg2 = nil)
-
warn('Config#value is deprecated; use Config#get_value')
-
if arg2.nil?
-
section, key = 'default', arg1
-
else
-
section, key = arg1, arg2
-
end
-
section ||= 'default'
-
section = 'default' if section.empty?
-
get_key_string(section, key)
-
end
-
-
1
def add_value(section, key, value)
-
check_modify
-
(@data[section] ||= {})[key] = value
-
end
-
-
1
def [](section)
-
@data[section] || {}
-
end
-
-
1
def section(name)
-
warn('Config#section is deprecated; use Config#[]')
-
@data[name] || {}
-
end
-
-
1
def []=(section, pairs)
-
check_modify
-
@data[section] ||= {}
-
pairs.each do |key, value|
-
self.add_value(section, key, value)
-
end
-
end
-
-
1
def sections
-
@data.keys
-
end
-
-
1
def to_s
-
ary = []
-
@data.keys.sort.each do |section|
-
ary << "[ #{section} ]\n"
-
@data[section].keys.each do |key|
-
ary << "#{key}=#{@data[section][key]}\n"
-
end
-
ary << "\n"
-
end
-
ary.join
-
end
-
-
1
def each
-
@data.each do |section, hash|
-
hash.each do |key, value|
-
yield [section, key, value]
-
end
-
end
-
end
-
-
1
def inspect
-
"#<#{self.class.name} sections=#{sections.inspect}>"
-
end
-
-
1
protected
-
-
1
def data
-
@data
-
end
-
-
1
private
-
-
1
def initialize_copy(other)
-
@data = other.data.dup
-
end
-
-
1
def check_modify
-
raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
-
end
-
-
1
def get_key_string(section, key)
-
Config.get_key_string(@data, section, key)
-
end
-
end
-
end
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space predefined Digest subclasses
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: digest.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class Digest
-
-
1
alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
-
1
if OPENSSL_VERSION_NUMBER > 0x00908000
-
1
alg += %w(SHA224 SHA256 SHA384 SHA512)
-
end
-
-
# Return the +data+ hash computed with +name+ Digest. +name+ is either the
-
# long name or short name of a supported digest algorithm.
-
#
-
# === Examples
-
#
-
# OpenSSL::Digest.digest("SHA256, "abc")
-
#
-
# which is equivalent to:
-
#
-
# OpenSSL::Digest::SHA256.digest("abc")
-
-
1
def self.digest(name, data)
-
super(data, name)
-
end
-
-
1
alg.each{|name|
-
13
klass = Class.new(Digest){
-
13
define_method(:initialize){|*data|
-
if data.length > 1
-
raise ArgumentError,
-
"wrong number of arguments (#{data.length} for 1)"
-
end
-
super(name, data.first)
-
}
-
}
-
26
singleton = (class << klass; self; end)
-
13
singleton.class_eval{
-
13
define_method(:digest){|data| Digest.digest(name, data) }
-
13
define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
-
}
-
13
const_set(name, klass)
-
}
-
-
# This class is only provided for backwards compatibility. Use OpenSSL::Digest in the future.
-
1
class Digest < Digest
-
1
def initialize(*args)
-
# add warning
-
super(*args)
-
end
-
end
-
-
end # Digest
-
end # OpenSSL
-
-
=begin
-
= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: ssl-internal.rb 29189 2010-09-06 01:53:00Z nahi $
-
=end
-
-
1
require "openssl/buffering"
-
1
require "fcntl"
-
-
1
module OpenSSL
-
1
module SSL
-
1
class SSLContext
-
1
DEFAULT_PARAMS = {
-
:ssl_version => "SSLv23",
-
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
-
:ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
-
:options => OpenSSL::SSL::OP_ALL,
-
}
-
-
1
DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
-
1
DEFAULT_CERT_STORE.set_default_paths
-
1
if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
-
1
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
-
end
-
-
1
def set_params(params={})
-
params = DEFAULT_PARAMS.merge(params)
-
params.each{|name, value| self.__send__("#{name}=", value) }
-
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
-
unless self.ca_file or self.ca_path or self.cert_store
-
self.cert_store = DEFAULT_CERT_STORE
-
end
-
end
-
return params
-
end
-
end
-
-
1
module SocketForwarder
-
1
def addr
-
to_io.addr
-
end
-
-
1
def peeraddr
-
to_io.peeraddr
-
end
-
-
1
def setsockopt(level, optname, optval)
-
to_io.setsockopt(level, optname, optval)
-
end
-
-
1
def getsockopt(level, optname)
-
to_io.getsockopt(level, optname)
-
end
-
-
1
def fcntl(*args)
-
to_io.fcntl(*args)
-
end
-
-
1
def closed?
-
to_io.closed?
-
end
-
-
1
def do_not_reverse_lookup=(flag)
-
to_io.do_not_reverse_lookup = flag
-
end
-
end
-
-
1
module Nonblock
-
1
def initialize(*args)
-
flag = File::NONBLOCK
-
flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
-
@io.fcntl(Fcntl::F_SETFL, flag)
-
super
-
end
-
end
-
-
1
def verify_certificate_identity(cert, hostname)
-
should_verify_common_name = true
-
cert.extensions.each{|ext|
-
next if ext.oid != "subjectAltName"
-
ext.value.split(/,\s+/).each{|general_name|
-
if /\ADNS:(.*)/ =~ general_name
-
should_verify_common_name = false
-
reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
-
return true if /\A#{reg}\z/i =~ hostname
-
elsif /\AIP Address:(.*)/ =~ general_name
-
should_verify_common_name = false
-
return true if $1 == hostname
-
end
-
}
-
}
-
if should_verify_common_name
-
cert.subject.to_a.each{|oid, value|
-
if oid == "CN"
-
reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
-
return true if /\A#{reg}\z/i =~ hostname
-
end
-
}
-
end
-
return false
-
end
-
1
module_function :verify_certificate_identity
-
-
1
class SSLSocket
-
1
include Buffering
-
1
include SocketForwarder
-
1
include Nonblock
-
-
1
def post_connection_check(hostname)
-
unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
-
raise SSLError, "hostname does not match the server certificate"
-
end
-
return true
-
end
-
-
1
def session
-
SSL::Session.new(self)
-
rescue SSL::Session::SessionError
-
nil
-
end
-
end
-
-
1
class SSLServer
-
1
include SocketForwarder
-
1
attr_accessor :start_immediately
-
-
1
def initialize(svr, ctx)
-
@svr = svr
-
@ctx = ctx
-
unless ctx.session_id_context
-
session_id = OpenSSL::Digest::MD5.hexdigest($0)
-
@ctx.session_id_context = session_id
-
end
-
@start_immediately = true
-
end
-
-
1
def to_io
-
@svr
-
end
-
-
1
def listen(backlog=5)
-
@svr.listen(backlog)
-
end
-
-
1
def shutdown(how=Socket::SHUT_RDWR)
-
@svr.shutdown(how)
-
end
-
-
1
def accept
-
sock = @svr.accept
-
begin
-
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
-
ssl.sync_close = true
-
ssl.accept if @start_immediately
-
ssl
-
rescue SSLError => ex
-
sock.close
-
raise ex
-
end
-
end
-
-
1
def close
-
@svr.close
-
end
-
end
-
end
-
end
-
=begin
-
= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: x509-internal.rb 32663 2011-07-25 04:51:26Z nahi $
-
=end
-
-
1
module OpenSSL
-
1
module X509
-
1
class ExtensionFactory
-
1
def create_extension(*arg)
-
if arg.size > 1
-
create_ext(*arg)
-
else
-
send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
-
end
-
end
-
-
1
def create_ext_from_array(ary)
-
raise ExtensionError, "unexpected array form" if ary.size > 3
-
create_ext(ary[0], ary[1], ary[2])
-
end
-
-
1
def create_ext_from_string(str) # "oid = critical, value"
-
oid, value = str.split(/=/, 2)
-
oid.strip!
-
value.strip!
-
create_ext(oid, value)
-
end
-
-
1
def create_ext_from_hash(hash)
-
create_ext(hash["oid"], hash["value"], hash["critical"])
-
end
-
end
-
-
1
class Extension
-
1
def to_s # "oid = critical, value"
-
str = self.oid
-
str << " = "
-
str << "critical, " if self.critical?
-
str << self.value.gsub(/\n/, ", ")
-
end
-
-
1
def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
-
{"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
-
end
-
-
1
def to_a
-
[ self.oid, self.value, self.critical? ]
-
end
-
end
-
-
1
class Name
-
1
module RFC2253DN
-
1
Special = ',=+<>#;'
-
1
HexChar = /[0-9a-fA-F]/
-
1
HexPair = /#{HexChar}#{HexChar}/
-
1
HexString = /#{HexPair}+/
-
1
Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
-
1
StringChar = /[^#{Special}\\"]/
-
1
QuoteChar = /[^\\"]/
-
1
AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
-
1
AttributeValue = /
-
(?!["#])((?:#{StringChar}|#{Pair})*)|
-
\#(#{HexString})|
-
"((?:#{QuoteChar}|#{Pair})*)"
-
/x
-
1
TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
-
1
module_function
-
-
1
def expand_pair(str)
-
return nil unless str
-
return str.gsub(Pair){
-
pair = $&
-
case pair.size
-
when 2 then pair[1,1]
-
when 3 then Integer("0x#{pair[1,2]}").chr
-
else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
-
end
-
}
-
end
-
-
1
def expand_hexstring(str)
-
return nil unless str
-
der = str.gsub(HexPair){$&.to_i(16).chr }
-
a1 = OpenSSL::ASN1.decode(der)
-
return a1.value, a1.tag
-
end
-
-
1
def expand_value(str1, str2, str3)
-
value = expand_pair(str1)
-
value, tag = expand_hexstring(str2) unless value
-
value = expand_pair(str3) unless value
-
return value, tag
-
end
-
-
1
def scan(dn)
-
str = dn
-
ary = []
-
while true
-
if md = TypeAndValue.match(str)
-
remain = md.post_match
-
type = md[1]
-
value, tag = expand_value(md[2], md[3], md[4]) rescue nil
-
if value
-
type_and_value = [type, value]
-
type_and_value.push(tag) if tag
-
ary.unshift(type_and_value)
-
if remain.length > 2 && remain[0] == ?,
-
str = remain[1..-1]
-
next
-
elsif remain.length > 2 && remain[0] == ?+
-
raise OpenSSL::X509::NameError,
-
"multi-valued RDN is not supported: #{dn}"
-
elsif remain.empty?
-
break
-
end
-
end
-
end
-
msg_dn = dn[0, dn.length - str.length] + " =>" + str
-
raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
-
end
-
return ary
-
end
-
end
-
-
1
class << self
-
1
def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
-
ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
-
self.new(ary, template)
-
end
-
-
1
def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
-
ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
-
self.new(ary, template)
-
end
-
-
1
alias parse parse_openssl
-
end
-
end
-
-
1
class StoreContext
-
1
def cleanup
-
warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
-
end
-
end
-
end
-
end
-
# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
1
require 'rubygems'
-
1
begin
-
1
gem 'rake'
-
rescue Gem::LoadError
-
end
-
-
1
require 'rake/packagetask'
-
-
##
-
# Create a package based upon a Gem::Specification. Gem packages, as well as
-
# zip files and tar/gzipped packages can be produced by this task.
-
#
-
# In addition to the Rake targets generated by Rake::PackageTask, a
-
# Gem::PackageTask will also generate the following tasks:
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>]
-
# Create a RubyGems package with the given name and version.
-
#
-
# Example using a Gem::Specification:
-
#
-
# require 'rubygems'
-
# require 'rubygems/package_task'
-
#
-
# spec = Gem::Specification.new do |s|
-
# s.platform = Gem::Platform::RUBY
-
# s.summary = "Ruby based make-like utility."
-
# s.name = 'rake'
-
# s.version = PKG_VERSION
-
# s.requirements << 'none'
-
# s.require_path = 'lib'
-
# s.autorequire = 'rake'
-
# s.files = PKG_FILES
-
# s.description = <<-EOF
-
# Rake is a Make-like program implemented in Ruby. Tasks
-
# and dependencies are specified in standard Ruby syntax.
-
# EOF
-
# end
-
#
-
# Gem::PackageTask.new(spec) do |pkg|
-
# pkg.need_zip = true
-
# pkg.need_tar = true
-
# end
-
-
1
class Gem::PackageTask < Rake::PackageTask
-
-
##
-
# Ruby Gem::Specification containing the metadata for this package. The
-
# name, version and package_files are automatically determined from the
-
# gemspec and don't need to be explicitly provided.
-
-
1
attr_accessor :gem_spec
-
-
##
-
# Create a Gem Package task library. Automatically define the gem if a
-
# block is given. If no block is supplied, then #define needs to be called
-
# to define the task.
-
-
1
def initialize(gem_spec)
-
1
init gem_spec
-
1
yield self if block_given?
-
1
define if block_given?
-
end
-
-
##
-
# Initialization tasks without the "yield self" or define operations.
-
-
1
def init(gem)
-
1
super gem.full_name, :noversion
-
1
@gem_spec = gem
-
1
@package_files += gem_spec.files if gem_spec.files
-
end
-
-
##
-
# Create the Rake tasks and actions specified by this Gem::PackageTask.
-
# (+define+ is automatically called if a block is given to +new+).
-
-
1
def define
-
1
super
-
-
1
task :package => [:gem]
-
-
1
gem_file = File.basename gem_spec.cache_file
-
1
gem_path = File.join package_dir, gem_file
-
1
gem_dir = File.join package_dir, gem_spec.full_name
-
-
1
desc "Build the gem file #{gem_file}"
-
1
task :gem => [gem_path]
-
-
1
trace = Rake.application.options.trace
-
1
Gem.configuration.verbose = trace
-
-
1
file gem_path => [package_dir, gem_dir] + @gem_spec.files do
-
chdir(gem_dir) do
-
when_writing "Creating #{gem_spec.file_name}" do
-
Gem::Builder.new(gem_spec).build
-
verbose trace do
-
mv gem_file, '..'
-
end
-
end
-
end
-
end
-
end
-
-
end
-
-
# = Secure random number generator interface.
-
#
-
# This library is an interface for secure random number generator which is
-
# suitable for generating session key in HTTP cookies, etc.
-
#
-
# It supports following secure random number generators.
-
#
-
# * openssl
-
# * /dev/urandom
-
# * Win32
-
#
-
# == Example
-
#
-
# # random hexadecimal string.
-
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
-
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
-
# p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
-
# p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
-
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
-
# ...
-
#
-
# # random base64 string.
-
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
-
# p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
-
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
-
# p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
-
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
-
# p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
-
# ...
-
#
-
# # random binary string.
-
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
-
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
-
# ...
-
-
1
begin
-
1
require 'openssl'
-
rescue LoadError
-
end
-
-
1
module SecureRandom
-
# SecureRandom.random_bytes generates a random binary string.
-
#
-
# The argument _n_ specifies the length of the result string.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain any byte: "\x00" - "\xff".
-
#
-
# p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
-
# p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
1
def self.random_bytes(n=nil)
-
n ||= 16
-
-
if defined? OpenSSL::Random
-
@pid = 0 if !defined?(@pid)
-
pid = $$
-
if @pid != pid
-
now = Time.now
-
ary = [now.to_i, now.nsec, @pid, pid]
-
OpenSSL::Random.seed(ary.to_s)
-
@pid = pid
-
end
-
return OpenSSL::Random.random_bytes(n)
-
end
-
-
if !defined?(@has_urandom) || @has_urandom
-
flags = File::RDONLY
-
flags |= File::NONBLOCK if defined? File::NONBLOCK
-
flags |= File::NOCTTY if defined? File::NOCTTY
-
begin
-
File.open("/dev/urandom", flags) {|f|
-
unless f.stat.chardev?
-
raise Errno::ENOENT
-
end
-
@has_urandom = true
-
ret = f.readpartial(n)
-
if ret.length != n
-
raise NotImplementedError, "Unexpected partial read from random device"
-
end
-
return ret
-
}
-
rescue Errno::ENOENT
-
@has_urandom = false
-
end
-
end
-
-
if !defined?(@has_win32)
-
begin
-
require 'Win32API'
-
-
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
-
@crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')
-
-
hProvStr = " " * 4
-
prov_rsa_full = 1
-
crypt_verifycontext = 0xF0000000
-
-
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
-
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
-
end
-
@hProv, = hProvStr.unpack('L')
-
-
@has_win32 = true
-
rescue LoadError
-
@has_win32 = false
-
end
-
end
-
if @has_win32
-
bytes = " ".force_encoding("ASCII-8BIT") * n
-
if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
-
raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
-
end
-
return bytes
-
end
-
-
raise NotImplementedError, "No random device"
-
end
-
-
# SecureRandom.hex generates a random hex string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is twice of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain 0-9 and a-f.
-
#
-
# p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
-
# p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
1
def self.hex(n=nil)
-
random_bytes(n).unpack("H*")[0]
-
end
-
-
# SecureRandom.base64 generates a random base64 string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is about 4/3 of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
-
#
-
# p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
-
# p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
#
-
# See RFC 3548 for the definition of base64.
-
1
def self.base64(n=nil)
-
[random_bytes(n)].pack("m*").delete("\n")
-
end
-
-
# SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is about 4/3 of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The boolean argument _padding_ specifies the padding.
-
# If it is false or nil, padding is not generated.
-
# Otherwise padding is generated.
-
# By default, padding is not generated because "=" may be used as a URL delimiter.
-
#
-
# The result may contain A-Z, a-z, 0-9, "-" and "_".
-
# "=" is also used if _padding_ is true.
-
#
-
# p SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
-
# p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
-
#
-
# p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
-
# p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
#
-
# See RFC 3548 for the definition of URL-safe base64.
-
1
def self.urlsafe_base64(n=nil, padding=false)
-
s = [random_bytes(n)].pack("m*")
-
s.delete!("\n")
-
s.tr!("+/", "-_")
-
s.delete!("=") if !padding
-
s
-
end
-
-
# SecureRandom.random_number generates a random number.
-
#
-
# If a positive integer is given as _n_,
-
# SecureRandom.random_number returns an integer:
-
# 0 <= SecureRandom.random_number(n) < n.
-
#
-
# p SecureRandom.random_number(100) #=> 15
-
# p SecureRandom.random_number(100) #=> 88
-
#
-
# If 0 is given or an argument is not given,
-
# SecureRandom.random_number returns a float:
-
# 0.0 <= SecureRandom.random_number() < 1.0.
-
#
-
# p SecureRandom.random_number #=> 0.596506046187744
-
# p SecureRandom.random_number #=> 0.350621695741409
-
#
-
1
def self.random_number(n=0)
-
if 0 < n
-
hex = n.to_s(16)
-
hex = '0' + hex if (hex.length & 1) == 1
-
bin = [hex].pack("H*")
-
mask = bin[0].ord
-
mask |= mask >> 1
-
mask |= mask >> 2
-
mask |= mask >> 4
-
begin
-
rnd = SecureRandom.random_bytes(bin.length)
-
rnd[0] = (rnd[0].ord & mask).chr
-
end until rnd < bin
-
rnd.unpack("H*")[0].hex
-
else
-
# assumption: Float::MANT_DIG <= 64
-
i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
-
Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
-
end
-
end
-
-
# SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier).
-
#
-
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
-
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
-
# p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
-
#
-
# The version 4 UUID is purely random (except the version).
-
# It doesn't contain meaningful information such as MAC address, time, etc.
-
#
-
# See RFC 4122 for details of UUID.
-
#
-
1
def self.uuid
-
ary = self.random_bytes(16).unpack("NnnnnN")
-
ary[2] = (ary[2] & 0x0fff) | 0x4000
-
ary[3] = (ary[3] & 0x3fff) | 0x8000
-
"%08x-%04x-%04x-%04x-%04x%08x" % ary
-
end
-
-
# Following code is based on David Garamond's GUID library for Ruby.
-
1
def self.lastWin32ErrorMessage # :nodoc:
-
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
-
format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
-
format_message_ignore_inserts = 0x00000200
-
format_message_from_system = 0x00001000
-
-
code = get_last_error.call
-
msg = "\0" * 1024
-
len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
-
msg[0, len].tr("\r", '').chomp
-
end
-
end
-
#
-
# tempfile - manipulates temporary files
-
#
-
# $Id: tempfile.rb 33089 2011-08-26 23:54:49Z drbrain $
-
#
-
-
1
require 'delegate'
-
1
require 'tmpdir'
-
1
require 'thread'
-
-
# A utility class for managing temporary files. When you create a Tempfile
-
# object, it will create a temporary file with a unique filename. A Tempfile
-
# objects behaves just like a File object, and you can perform all the usual
-
# file operations on it: reading data, writing data, changing its permissions,
-
# etc. So although this class does not explicitly document all instance methods
-
# supported by File, you can in fact call any File instance method on a
-
# Tempfile object.
-
#
-
# == Synopsis
-
#
-
# require 'tempfile'
-
#
-
# file = Tempfile.new('foo')
-
# file.path # => A unique filename in the OS's temp directory,
-
# # e.g.: "/tmp/foo.24722.0"
-
# # This filename contains 'foo' in its basename.
-
# file.write("hello world")
-
# file.rewind
-
# file.read # => "hello world"
-
# file.close
-
# file.unlink # deletes the temp file
-
#
-
# == Good practices
-
#
-
# === Explicit close
-
#
-
# When a Tempfile object is garbage collected, or when the Ruby interpreter
-
# exits, its associated temporary file is automatically deleted. This means
-
# that's it's unnecessary to explicitly delete a Tempfile after use, though
-
# it's good practice to do so: not explicitly deleting unused Tempfiles can
-
# potentially leave behind large amounts of tempfiles on the filesystem
-
# until they're garbage collected. The existence of these temp files can make
-
# it harder to determine a new Tempfile filename.
-
#
-
# Therefore, one should always call #unlink or close in an ensure block, like
-
# this:
-
#
-
# file = Tempfile.new('foo')
-
# begin
-
# ...do something with file...
-
# ensure
-
# file.close
-
# file.unlink # deletes the temp file
-
# end
-
#
-
# === Unlink after creation
-
#
-
# On POSIX systems, it's possible to unlink a file right after creating it,
-
# and before closing it. This removes the filesystem entry without closing
-
# the file handle, so it ensures that only the processes that already had
-
# the file handle open can access the file's contents. It's strongly
-
# recommended that you do this if you do not want any other processes to
-
# be able to read from or write to the Tempfile, and you do not need to
-
# know the Tempfile's filename either.
-
#
-
# For example, a practical use case for unlink-after-creation would be this:
-
# you need a large byte buffer that's too large to comfortably fit in RAM,
-
# e.g. when you're writing a web server and you want to buffer the client's
-
# file upload data.
-
#
-
# Please refer to #unlink for more information and a code example.
-
#
-
# == Minor notes
-
#
-
# Tempfile's filename picking method is both thread-safe and inter-process-safe:
-
# it guarantees that no other threads or processes will pick the same filename.
-
#
-
# Tempfile itself however may not be entirely thread-safe. If you access the
-
# same Tempfile object from multiple threads then you should protect it with a
-
# mutex.
-
1
class Tempfile < DelegateClass(File)
-
1
MAX_TRY = 10 # :nodoc:
-
1
include Dir::Tmpname
-
-
# call-seq:
-
# new(basename, [tmpdir = Dir.tmpdir], [options])
-
#
-
# Creates a temporary file with permissions 0600 (= only readable and
-
# writable by the owner) and opens it with mode "w+".
-
#
-
# The +basename+ parameter is used to determine the name of the
-
# temporary file. You can either pass a String or an Array with
-
# 2 String elements. In the former form, the temporary file's base
-
# name will begin with the given string. In the latter form,
-
# the temporary file's base name will begin with the array's first
-
# element, and end with the second element. For example:
-
#
-
# file = Tempfile.new('hello')
-
# file.path # => something like: "/tmp/hello2843-8392-92849382--0"
-
#
-
# # Use the Array form to enforce an extension in the filename:
-
# file = Tempfile.new(['hello', '.jpg'])
-
# file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
-
#
-
# The temporary file will be placed in the directory as specified
-
# by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+.
-
# When $SAFE > 0 and the given +tmpdir+ is tainted, it uses
-
# '/tmp' as the temporary directory. Please note that ENV values
-
# are tainted by default, and +Dir.tmpdir+'s return value might
-
# come from environment variables (e.g. <tt>$TMPDIR</tt>).
-
#
-
# file = Tempfile.new('hello', '/home/aisaka')
-
# file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0"
-
#
-
# You can also pass an options hash. Under the hood, Tempfile creates
-
# the temporary file using +File.open+. These options will be passed to
-
# +File.open+. This is mostly useful for specifying encoding
-
# options, e.g.:
-
#
-
# Tempfile.new('hello', '/home/aisaka', :encoding => 'ascii-8bit')
-
#
-
# # You can also omit the 'tmpdir' parameter:
-
# Tempfile.new('hello', :encoding => 'ascii-8bit')
-
#
-
# === Exceptions
-
#
-
# If Tempfile.new cannot find a unique filename within a limited
-
# number of tries, then it will raise an exception.
-
1
def initialize(basename, *rest)
-
@data = []
-
@clean_proc = Remover.new(@data)
-
ObjectSpace.define_finalizer(self, @clean_proc)
-
-
create(basename, *rest) do |tmpname, n, opts|
-
mode = File::RDWR|File::CREAT|File::EXCL
-
perm = 0600
-
if opts
-
mode |= opts.delete(:mode) || 0
-
opts[:perm] = perm
-
perm = nil
-
else
-
opts = perm
-
end
-
self.class.locking(tmpname) do
-
@data[1] = @tmpfile = File.open(tmpname, mode, opts)
-
@data[0] = @tmpname = tmpname
-
end
-
@mode = mode & ~(File::CREAT|File::EXCL)
-
perm or opts.freeze
-
@opts = opts
-
end
-
-
super(@tmpfile)
-
end
-
-
# Opens or reopens the file with mode "r+".
-
1
def open
-
@tmpfile.close if @tmpfile
-
@tmpfile = File.open(@tmpname, @mode, @opts)
-
@data[1] = @tmpfile
-
__setobj__(@tmpfile)
-
end
-
-
1
def _close # :nodoc:
-
@tmpfile.close if @tmpfile
-
@tmpfile = nil
-
@data[1] = nil if @data
-
end
-
1
protected :_close
-
-
# Closes the file. If +unlink_now+ is true, then the file will be unlinked
-
# (deleted) after closing. Of course, you can choose to later call #unlink
-
# if you do not unlink it now.
-
#
-
# If you don't explicitly unlink the temporary file, the removal
-
# will be delayed until the object is finalized.
-
1
def close(unlink_now=false)
-
if unlink_now
-
close!
-
else
-
_close
-
end
-
end
-
-
# Closes and unlinks (deletes) the file. Has the same effect as called
-
# <tt>close(true)</tt>.
-
1
def close!
-
_close
-
unlink
-
ObjectSpace.undefine_finalizer(self)
-
end
-
-
# Unlinks (deletes) the file from the filesystem. One should always unlink
-
# the file after using it, as is explained in the "Explicit close" good
-
# practice section in the Tempfile overview:
-
#
-
# file = Tempfile.new('foo')
-
# begin
-
# ...do something with file...
-
# ensure
-
# file.close
-
# file.unlink # deletes the temp file
-
# end
-
#
-
# === Unlink-before-close
-
#
-
# On POSIX systems it's possible to unlink a file before closing it. This
-
# practice is explained in detail in the Tempfile overview (section
-
# "Unlink after creation"); please refer there for more information.
-
#
-
# However, unlink-before-close may not be supported on non-POSIX operating
-
# systems. Microsoft Windows is the most notable case: unlinking a non-closed
-
# file will result in an error, which this method will silently ignore. If
-
# you want to practice unlink-before-close whenever possible, then you should
-
# write code like this:
-
#
-
# file = Tempfile.new('foo')
-
# file.unlink # On Windows this silently fails.
-
# begin
-
# ... do something with file ...
-
# ensure
-
# file.close! # Closes the file handle. If the file wasn't unlinked
-
# # because #unlink failed, then this method will attempt
-
# # to do so again.
-
# end
-
1
def unlink
-
# keep this order for thread safeness
-
return unless @tmpname
-
begin
-
if File.exist?(@tmpname)
-
File.unlink(@tmpname)
-
end
-
# remove tmpname from remover
-
@data[0] = @data[2] = nil
-
@tmpname = nil
-
rescue Errno::EACCES
-
# may not be able to unlink on Windows; just ignore
-
end
-
end
-
1
alias delete unlink
-
-
# Returns the full path name of the temporary file.
-
# This will be nil if #unlink has been called.
-
1
def path
-
@tmpname
-
end
-
-
# Returns the size of the temporary file. As a side effect, the IO
-
# buffer is flushed before determining the size.
-
1
def size
-
if @tmpfile
-
@tmpfile.flush
-
@tmpfile.stat.size
-
elsif @tmpname
-
File.size(@tmpname)
-
else
-
0
-
end
-
end
-
1
alias length size
-
-
# :stopdoc:
-
1
class Remover
-
1
def initialize(data)
-
@pid = $$
-
@data = data
-
end
-
-
1
def call(*args)
-
if @pid == $$
-
path, tmpfile = *@data
-
-
STDERR.print "removing ", path, "..." if $DEBUG
-
-
tmpfile.close if tmpfile
-
-
# keep this order for thread safeness
-
if path
-
File.unlink(path) if File.exist?(path)
-
end
-
-
STDERR.print "done\n" if $DEBUG
-
end
-
end
-
end
-
# :startdoc:
-
-
1
class << self
-
# Creates a new Tempfile.
-
#
-
# If no block is given, this is a synonym for Tempfile.new.
-
#
-
# If a block is given, then a Tempfile object will be constructed,
-
# and the block is run with said object as argument. The Tempfile
-
# oject will be automatically closed after the block terminates.
-
# The call returns the value of the block.
-
#
-
# In any case, all arguments (+*args+) will be passed to Tempfile.new.
-
#
-
# Tempfile.open('foo', '/home/temp') do |f|
-
# ... do something with f ...
-
# end
-
#
-
# # Equivalent:
-
# f = Tempfile.open('foo', '/home/temp')
-
# begin
-
# ... do something with f ...
-
# ensure
-
# f.close
-
# end
-
1
def open(*args)
-
tempfile = new(*args)
-
-
if block_given?
-
begin
-
yield(tempfile)
-
ensure
-
tempfile.close
-
end
-
else
-
tempfile
-
end
-
end
-
-
# :stopdoc:
-
-
# yields with locking for +tmpname+ and returns the result of the
-
# block.
-
1
def locking(tmpname)
-
lock = tmpname + '.lock'
-
mkdir(lock)
-
yield
-
ensure
-
rmdir(lock) if lock
-
end
-
-
1
def mkdir(*args)
-
Dir.mkdir(*args)
-
end
-
-
1
def rmdir(*args)
-
Dir.rmdir(*args)
-
end
-
end
-
end
-
-
1
if __FILE__ == $0
-
# $DEBUG = true
-
f = Tempfile.new("foo")
-
f.print("foo\n")
-
f.close
-
f.open
-
p f.gets # => "foo\n"
-
f.close!
-
end
-
#
-
# tmpdir - retrieve temporary directory path
-
#
-
# $Id: tmpdir.rb 31635 2011-05-18 21:19:18Z drbrain $
-
#
-
-
1
require 'fileutils'
-
1
begin
-
1
require 'etc.so'
-
rescue LoadError
-
end
-
-
1
class Dir
-
-
1
@@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
-
-
##
-
# Returns the operating system's temporary file path.
-
-
1
def Dir::tmpdir
-
tmp = '.'
-
if $SAFE > 0
-
tmp = @@systmpdir
-
else
-
for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp']
-
if dir and stat = File.stat(dir) and stat.directory? and stat.writable?
-
tmp = dir
-
break
-
end rescue nil
-
end
-
File.expand_path(tmp)
-
end
-
end
-
-
# Dir.mktmpdir creates a temporary directory.
-
#
-
# The directory is created with 0700 permission.
-
#
-
# The prefix and suffix of the name of the directory is specified by
-
# the optional first argument, <i>prefix_suffix</i>.
-
# - If it is not specified or nil, "d" is used as the prefix and no suffix is used.
-
# - If it is a string, it is used as the prefix and no suffix is used.
-
# - If it is an array, first element is used as the prefix and second element is used as a suffix.
-
#
-
# Dir.mktmpdir {|dir| dir is ".../d..." }
-
# Dir.mktmpdir("foo") {|dir| dir is ".../foo..." }
-
# Dir.mktmpdir(["foo", "bar"]) {|dir| dir is ".../foo...bar" }
-
#
-
# The directory is created under Dir.tmpdir or
-
# the optional second argument <i>tmpdir</i> if non-nil value is given.
-
#
-
# Dir.mktmpdir {|dir| dir is "#{Dir.tmpdir}/d..." }
-
# Dir.mktmpdir(nil, "/var/tmp") {|dir| dir is "/var/tmp/d..." }
-
#
-
# If a block is given,
-
# it is yielded with the path of the directory.
-
# The directory and its contents are removed
-
# using FileUtils.remove_entry_secure before Dir.mktmpdir returns.
-
# The value of the block is returned.
-
#
-
# Dir.mktmpdir {|dir|
-
# # use the directory...
-
# open("#{dir}/foo", "w") { ... }
-
# }
-
#
-
# If a block is not given,
-
# The path of the directory is returned.
-
# In this case, Dir.mktmpdir doesn't remove the directory.
-
#
-
# dir = Dir.mktmpdir
-
# begin
-
# # use the directory...
-
# open("#{dir}/foo", "w") { ... }
-
# ensure
-
# # remove the directory.
-
# FileUtils.remove_entry_secure dir
-
# end
-
#
-
1
def Dir.mktmpdir(prefix_suffix=nil, *rest)
-
path = Tmpname.create(prefix_suffix || "d", *rest) {|n| mkdir(n, 0700)}
-
if block_given?
-
begin
-
yield path
-
ensure
-
FileUtils.remove_entry_secure path
-
end
-
else
-
path
-
end
-
end
-
-
1
module Tmpname # :nodoc:
-
1
module_function
-
-
1
def tmpdir
-
Dir.tmpdir
-
end
-
-
1
def make_tmpname(prefix_suffix, n)
-
case prefix_suffix
-
when String
-
prefix = prefix_suffix
-
suffix = ""
-
when Array
-
prefix = prefix_suffix[0]
-
suffix = prefix_suffix[1]
-
else
-
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
-
end
-
t = Time.now.strftime("%Y%m%d")
-
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
-
path << "-#{n}" if n
-
path << suffix
-
end
-
-
1
def create(basename, *rest)
-
if opts = Hash.try_convert(rest[-1])
-
opts = opts.dup if rest.pop.equal?(opts)
-
max_try = opts.delete(:max_try)
-
opts = [opts]
-
else
-
opts = []
-
end
-
tmpdir, = *rest
-
if $SAFE > 0 and tmpdir.tainted?
-
tmpdir = '/tmp'
-
else
-
tmpdir ||= tmpdir()
-
end
-
n = nil
-
begin
-
path = File.expand_path(make_tmpname(basename, n), tmpdir)
-
yield(path, n, *opts)
-
rescue Errno::EEXIST
-
n ||= 0
-
n += 1
-
retry if !max_try or n < max_try
-
raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
-
end
-
path
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2004-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
1
require 'simplecov'
-
1
SimpleCov.start
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'active_model/version'
-
-
1
module ActiveModel
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :AttributeMethods
-
1
autoload :BlockValidator, 'active_model/validator'
-
1
autoload :Callbacks
-
1
autoload :Conversion
-
1
autoload :Dirty
-
1
autoload :EachValidator, 'active_model/validator'
-
1
autoload :ForbiddenAttributesProtection
-
1
autoload :Lint
-
1
autoload :Model
-
1
autoload :DeprecatedMassAssignmentSecurity
-
1
autoload :Name, 'active_model/naming'
-
1
autoload :Naming
-
1
autoload :Observer, 'active_model/observing'
-
1
autoload :Observing
-
1
autoload :SecurePassword
-
1
autoload :Serialization
-
1
autoload :TestCase
-
1
autoload :Translation
-
1
autoload :Validations
-
1
autoload :Validator
-
-
1
eager_autoload do
-
1
autoload :Errors
-
end
-
-
1
module Serializers
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :JSON
-
1
autoload :Xml
-
end
-
end
-
-
1
def eager_load!
-
super
-
ActiveModel::Serializer.eager_load!
-
end
-
end
-
-
1
ActiveSupport.on_load(:i18n) do
-
1
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
-
end
-
-
1
module ActiveModel
-
# Raised when an attribute is not defined.
-
#
-
# class User < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# user = User.first
-
# user.pets.select(:id).first.user_id
-
# # => ActiveModel::MissingAttributeError: missing attribute: user_id
-
1
class MissingAttributeError < NoMethodError
-
end
-
# == Active \Model Attribute Methods
-
#
-
# <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and
-
# suffixes to your methods as well as handling the creation of Active Record
-
# like class methods such as +table_name+.
-
#
-
# The requirements to implement ActiveModel::AttributeMethods are to:
-
#
-
# * <tt>include ActiveModel::AttributeMethods</tt> in your object.
-
# * Call each Attribute Method module method you want to add, such as
-
# +attribute_method_suffix+ or +attribute_method_prefix+.
-
# * Call +define_attribute_methods+ after the other methods are called.
-
# * Define the various generic +_attribute+ methods that you have declared.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
-
# attribute_method_suffix '_contrived?'
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods :name
-
#
-
# attr_accessor :name
-
#
-
# private
-
#
-
# def attribute_contrived?(attr)
-
# true
-
# end
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
#
-
# def reset_attribute_to_default!(attr)
-
# send("#{attr}=", 'Default Name')
-
# end
-
# end
-
#
-
# Note that whenever you include ActiveModel::AttributeMethods in your class,
-
# it requires you to implement an +attributes+ method which returns a hash
-
# with each attribute name in your model as hash key and the attribute value as
-
# hash value.
-
#
-
# Hash keys must be strings.
-
1
module AttributeMethods
-
1
extend ActiveSupport::Concern
-
-
1
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
-
1
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
-
-
1
included do
-
7
class_attribute :attribute_aliases, :attribute_method_matchers, instance_writer: false
-
7
self.attribute_aliases = {}
-
7
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
-
end
-
-
1
module ClassMethods
-
# Declares a method available for all attributes with the given prefix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{prefix}#{attr}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute(#{attr}, *args, &block)
-
#
-
# An instance method <tt>#{prefix}attribute</tt> must exist and accept
-
# at least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.clear_name
-
# person.name # => nil
-
1
def attribute_method_prefix(*prefixes)
-
2
self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new prefix: prefix }
-
1
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given suffix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>attribute#{suffix}</tt> instance method must exist and accept at
-
# least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.name_short? # => true
-
1
def attribute_method_suffix(*suffixes)
-
9
self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new suffix: suffix }
-
3
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given prefix
-
# and suffix. Uses +method_missing+ and <tt>respond_to?</tt> to rewrite
-
# the method.
-
#
-
# #{prefix}#{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>#{prefix}attribute#{suffix}</tt> instance method must exist and
-
# accept at least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def reset_attribute_to_default!(attr)
-
# ...
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name # => 'Gem'
-
# person.reset_name_to_default!
-
# person.name # => 'Gemma'
-
1
def attribute_method_affix(*affixes)
-
2
self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
-
1
undefine_attribute_methods
-
end
-
-
-
# Allows you to make aliases for attributes.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_methods :name
-
#
-
# alias_attribute :nickname, :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.nickname # => "Bob"
-
# person.name_short? # => true
-
# person.nickname_short? # => true
-
1
def alias_attribute(new_name, old_name)
-
4
self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
-
4
attribute_method_matchers.each do |matcher|
-
4
matcher_new = matcher.method_name(new_name).to_s
-
4
matcher_old = matcher.method_name(old_name).to_s
-
4
define_proxy_call false, self, matcher_new, matcher_old
-
end
-
end
-
-
# Declares the attributes that should be prefixed and suffixed by
-
# ActiveModel::AttributeMethods.
-
#
-
# To use, pass attribute names (as strings or symbols), be sure to declare
-
# +define_attribute_methods+ after you define any prefix, suffix or affix
-
# methods, or they will not hook in.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name, :age, :address
-
# attribute_method_prefix 'clear_'
-
#
-
# # Call to define_attribute_methods must appear after the
-
# # attribute_method_prefix, attribute_method_suffix or
-
# # attribute_method_affix declares.
-
# define_attribute_methods :name, :age, :address
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# ...
-
# end
-
# end
-
1
def define_attribute_methods(*attr_names)
-
21
attr_names.flatten.each { |attr_name| define_attribute_method(attr_name) }
-
end
-
-
# Declares an attribute that should be prefixed and suffixed by
-
# ActiveModel::AttributeMethods.
-
#
-
# To use, pass an attribute name (as string or symbol), be sure to declare
-
# +define_attribute_method+ after you define any prefix, suffix or affix
-
# method, or they will not hook in.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
#
-
# # Call to define_attribute_method must appear after the
-
# # attribute_method_prefix, attribute_method_suffix or
-
# # attribute_method_affix declares.
-
# define_attribute_method :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.name_short? # => true
-
1
def define_attribute_method(attr_name)
-
16
attribute_method_matchers.each do |matcher|
-
28
method_name = matcher.method_name(attr_name)
-
-
28
unless instance_method_already_implemented?(method_name)
-
21
generate_method = "define_method_#{matcher.method_missing_target}"
-
-
21
if respond_to?(generate_method, true)
-
send(generate_method, attr_name)
-
else
-
21
define_proxy_call true, generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
-
end
-
end
-
end
-
16
attribute_method_matchers_cache.clear
-
end
-
-
# Removes all the previously dynamically defined methods from the class.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_method :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name_short? # => true
-
#
-
# Person.undefine_attribute_methods
-
#
-
# person.name_short? # => NoMethodError
-
1
def undefine_attribute_methods
-
6
generated_attribute_methods.module_eval do
-
8
instance_methods.each { |m| undef_method(m) }
-
end
-
6
attribute_method_matchers_cache.clear
-
end
-
-
# Returns true if the attribute methods defined have been generated.
-
1
def generated_attribute_methods #:nodoc:
-
66
@generated_attribute_methods ||= Module.new.tap { |mod| include mod }
-
end
-
-
1
protected
-
1
def instance_method_already_implemented?(method_name) #:nodoc:
-
28
generated_attribute_methods.method_defined?(method_name)
-
end
-
-
1
private
-
# The methods +method_missing+ and +respond_to?+ of this module are
-
# invoked often in a typical rails, both of which invoke the method
-
# +match_attribute_method?+. The latter method iterates through an
-
# array doing regular expression matches, which results in a lot of
-
# object creations. Most of the times it returns a +nil+ match. As the
-
# match result is always the same given a +method_name+, this cache is
-
# used to alleviate the GC, which ultimately also speeds up the app
-
# significantly (in our case our test suite finishes 10% faster with
-
# this cache).
-
1
def attribute_method_matchers_cache #:nodoc:
-
32
@attribute_method_matchers_cache ||= {}
-
end
-
-
1
def attribute_method_matcher(method_name) #:nodoc:
-
6
attribute_method_matchers_cache.fetch(method_name) do |name|
-
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
-
# will match every time.
-
4
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
-
4
match = nil
-
9
matchers.detect { |method| match = method.match(name) }
-
4
attribute_method_matchers_cache[name] = match
-
end
-
end
-
-
# Define a method `name` in `mod` that dispatches to `send`
-
# using the given `extra` args. This fallbacks `define_method`
-
# and `send` if the given names cannot be compiled.
-
1
def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
-
25
defn = if name =~ NAME_COMPILABLE_REGEXP
-
23
"def #{name}(*args)"
-
else
-
2
"define_method(:'#{name}') do |*args|"
-
end
-
-
25
extra = (extra.map!(&:inspect) << "*args").join(", ")
-
-
25
target = if send =~ CALL_COMPILABLE_REGEXP
-
24
"#{"self." unless include_private}#{send}(#{extra})"
-
else
-
1
"send(:'#{send}', #{extra})"
-
end
-
-
25
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
#{defn}
-
#{target}
-
end
-
RUBY
-
end
-
-
1
class AttributeMethodMatcher #:nodoc:
-
1
attr_reader :prefix, :suffix, :method_missing_target
-
-
1
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
-
-
1
def initialize(options = {})
-
15
if options[:prefix] == '' || options[:suffix] == ''
-
2
message = "Specifying an empty prefix/suffix for an attribute method is no longer " \
-
"necessary. If the un-prefixed/suffixed version of the method has not been " \
-
"defined when `define_attribute_methods` is called, it will be defined " \
-
"automatically."
-
2
ActiveSupport::Deprecation.warn message
-
end
-
-
15
@prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
-
15
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
-
15
@method_missing_target = "#{@prefix}attribute#{@suffix}"
-
15
@method_name = "#{prefix}%s#{suffix}"
-
end
-
-
1
def match(method_name)
-
5
if @regex =~ method_name
-
4
AttributeMethodMatch.new(method_missing_target, $1, method_name)
-
end
-
end
-
-
1
def method_name(attr_name)
-
36
@method_name % attr_name
-
end
-
-
1
def plain?
-
6
prefix.empty? && suffix.empty?
-
end
-
end
-
end
-
-
# Allows access to the object attributes, which are held in the
-
# <tt>@attributes</tt> hash, as though they were first-class methods. So a
-
# Person class with a name attribute can use Person#name and Person#name=
-
# and never directly use the attributes hash -- except for multiple assigns
-
# with ActiveRecord#attributes=. A Milestone class can also ask
-
# Milestone#completed? to test that the completed attribute is not +nil+
-
# or 0.
-
#
-
# It's also possible to instantiate related objects, so a Client class
-
# belonging to the clients table with a +master_id+ foreign key can
-
# instantiate master through Client#master.
-
1
def method_missing(method, *args, &block)
-
7
if respond_to_without_attributes?(method, true)
-
2
super
-
else
-
5
match = match_attribute_method?(method.to_s)
-
5
match ? attribute_missing(match, *args, &block) : super
-
end
-
end
-
-
# attribute_missing is like method_missing, but for attributes. When method_missing is
-
# called we check to see if there is a matching attribute method. If so, we call
-
# attribute_missing to dispatch the attribute. This method can be overloaded to
-
# customise the behaviour.
-
1
def attribute_missing(match, *args, &block)
-
2
__send__(match.target, match.attr_name, *args, &block)
-
end
-
-
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
-
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
-
# which will all return +true+.
-
1
alias :respond_to_without_attributes? :respond_to?
-
1
def respond_to?(method, include_private_methods = false)
-
9
if super
-
7
true
-
2
elsif !include_private_methods && super(method, true)
-
# If we're here then we haven't found among non-private methods
-
# but found among all methods. Which means that the given method is private.
-
1
false
-
else
-
1
!match_attribute_method?(method.to_s).nil?
-
end
-
end
-
-
1
protected
-
1
def attribute_method?(attr_name) #:nodoc:
-
6
respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
-
end
-
-
1
private
-
# Returns a struct representing the matching attribute method.
-
# The struct's attributes are prefix, base and suffix.
-
1
def match_attribute_method?(method_name)
-
6
match = self.class.send(:attribute_method_matcher, method_name)
-
6
match if match && attribute_method?(match.attr_name)
-
end
-
-
1
def missing_attribute(attr_name, stack)
-
raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
-
end
-
end
-
end
-
1
require 'active_support/callbacks'
-
-
1
module ActiveModel
-
# == Active \Model \Callbacks
-
#
-
# Provides an interface for any class to have Active Record like callbacks.
-
#
-
# Like the Active Record methods, the callback chain is aborted as soon as
-
# one of the methods in the chain returns +false+.
-
#
-
# First, extend ActiveModel::Callbacks from the class you are creating:
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# end
-
#
-
# Then define a list of methods that you want callbacks attached to:
-
#
-
# define_model_callbacks :create, :update
-
#
-
# This will provide all three standard callbacks (before, around and after)
-
# for both the <tt>:create</tt> and <tt>:update</tt> methods. To implement,
-
# you need to wrap the methods you want callbacks on in a block so that the
-
# callbacks get a chance to fire:
-
#
-
# def create
-
# run_callbacks :create do
-
# # Your create action methods here
-
# end
-
# end
-
#
-
# Then in your class, you can use the +before_create+, +after_create+ and
-
# +around_create+ methods, just as you would in an Active Record module.
-
#
-
# before_create :action_before_create
-
#
-
# def action_before_create
-
# # Your code here
-
# end
-
#
-
# When defining an around callback remember to yield to the block, otherwise
-
# it won't be executed:
-
#
-
# around_create :log_status
-
#
-
# def log_status
-
# puts 'going to call the block...'
-
# yield
-
# puts 'block successfully called.'
-
# end
-
#
-
# You can choose not to have all three callbacks by passing a hash to the
-
# +define_model_callbacks+ method.
-
#
-
# define_model_callbacks :create, only: [:after, :before]
-
#
-
# Would only create the +after_create+ and +before_create+ callback methods in
-
# your class.
-
1
module Callbacks
-
1
def self.extended(base) #:nodoc:
-
18
base.class_eval do
-
18
include ActiveSupport::Callbacks
-
end
-
end
-
-
# define_model_callbacks accepts the same options +define_callbacks+ does,
-
# in case you want to overwrite a default. Besides that, it also accepts an
-
# <tt>:only</tt> option, where you can choose if you want all types (before,
-
# around or after) or just some.
-
#
-
# define_model_callbacks :initializer, only: :after
-
#
-
# Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
-
# on that method call. To get around this you can call the define_model_callbacks
-
# method as many times as you need.
-
#
-
# define_model_callbacks :create, only: :after
-
# define_model_callbacks :update, only: :before
-
# define_model_callbacks :destroy, only: :around
-
#
-
# Would create +after_create+, +before_update+ and +around_destroy+ methods
-
# only.
-
#
-
# You can pass in a class to before_<type>, after_<type> and around_<type>,
-
# in which case the callback will call that class's <action>_<type> method
-
# passing the object that the callback is being called on.
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# define_model_callbacks :create
-
#
-
# before_create AnotherClass
-
# end
-
#
-
# class AnotherClass
-
# def self.before_create( obj )
-
# # obj is the MyModel instance that the callback is being called on
-
# end
-
# end
-
1
def define_model_callbacks(*callbacks)
-
9
options = callbacks.extract_options!
-
9
options = {
-
:terminator => "result == false",
-
:skip_after_callbacks_if_terminated => true,
-
:scope => [:kind, :name],
-
:only => [:before, :around, :after]
-
}.merge!(options)
-
-
9
types = Array(options.delete(:only))
-
-
9
callbacks.each do |callback|
-
9
define_callbacks(callback, options)
-
-
9
types.each do |type|
-
21
send("_define_#{type}_model_callback", self, callback)
-
end
-
end
-
end
-
-
1
private
-
-
1
def _define_before_model_callback(klass, callback) #:nodoc:
-
7
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.before_#{callback}(*args, &block)
-
set_callback(:#{callback}, :before, *args, &block)
-
end
-
CALLBACK
-
end
-
-
1
def _define_around_model_callback(klass, callback) #:nodoc:
-
7
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.around_#{callback}(*args, &block)
-
set_callback(:#{callback}, :around, *args, &block)
-
end
-
CALLBACK
-
end
-
-
1
def _define_after_model_callback(klass, callback) #:nodoc:
-
7
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
-
def self.after_#{callback}(*args, &block)
-
options = args.extract_options!
-
options[:prepend] = true
-
options[:if] = Array(options[:if]) << "value != false"
-
set_callback(:#{callback}, :after, *(args << options), &block)
-
end
-
CALLBACK
-
end
-
end
-
end
-
1
require 'active_support/inflector'
-
-
1
module ActiveModel
-
# == Active \Model Conversions
-
#
-
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
-
#
-
# Let's take for example this non-persisted object.
-
#
-
# class ContactMessage
-
# include ActiveModel::Conversion
-
#
-
# # ContactMessage are never persisted in the DB
-
# def persisted?
-
# false
-
# end
-
# end
-
#
-
# cm = ContactMessage.new
-
# cm.to_model == cm # => true
-
# cm.to_key # => nil
-
# cm.to_param # => nil
-
# cm.to_partial_path # => "contact_messages/contact_message"
-
1
module Conversion
-
1
extend ActiveSupport::Concern
-
-
# If your object is already designed to implement all of the Active Model
-
# you can use the default <tt>:to_model</tt> implementation, which simply
-
# returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Conversion
-
# end
-
#
-
# person = Person.new
-
# person.to_model == person # => true
-
#
-
# If your model does not act like an Active Model object, then you should
-
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
-
# your object with Active Model compliant methods.
-
1
def to_model
-
36
self
-
end
-
-
# Returns an Enumerable of all key attributes if any is set, regardless if
-
# the object is persisted or not. If there no key attributes, returns +nil+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.create
-
# person.to_key # => [1]
-
1
def to_key
-
5
key = respond_to?(:id) && id
-
5
key ? [key] : nil
-
end
-
-
# Returns a +string+ representing the object's key suitable for use in URLs,
-
# or +nil+ if <tt>persisted?</tt> is +false+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.create
-
# person.to_param # => "1"
-
1
def to_param
-
4
persisted? ? to_key.join('-') : nil
-
end
-
-
# Returns a +string+ identifying the path associated with the object.
-
# ActionPack uses this to find a suitable partial to represent the object.
-
#
-
# class Person
-
# include ActiveModel::Conversion
-
# end
-
#
-
# person = Person.new
-
# person.to_partial_path # => "people/person"
-
1
def to_partial_path
-
4
self.class._to_partial_path
-
end
-
-
1
module ClassMethods #:nodoc:
-
# Provide a class level cache for #to_partial_path. This is an
-
# internal method and should not be accessed directly.
-
1
def _to_partial_path #:nodoc:
-
@_to_partial_path ||= begin
-
4
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))
-
4
collection = ActiveSupport::Inflector.tableize(self)
-
4
"#{collection}/#{element}".freeze
-
4
end
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module DeprecatedMassAssignmentSecurity # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods # :nodoc:
-
1
def attr_protected(*args)
-
raise "`attr_protected` is extracted out of Rails into a gem. " \
-
"Please use new recommended protection model for params " \
-
1
"or add `protected_attributes` to your Gemfile to use old one."
-
end
-
-
1
def attr_accessible(*args)
-
raise "`attr_accessible` is extracted out of Rails into a gem. " \
-
"Please use new recommended protection model for params " \
-
1
"or add `protected_attributes` to your Gemfile to use old one."
-
end
-
end
-
end
-
end
-
1
require 'active_model/attribute_methods'
-
1
require 'active_support/hash_with_indifferent_access'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
module ActiveModel
-
# == Active \Model \Dirty
-
#
-
# Provides a way to track changes in your object in the same way as
-
# Active Record does.
-
#
-
# The requirements for implementing ActiveModel::Dirty are:
-
#
-
# * <tt>include ActiveModel::Dirty</tt> in your object.
-
# * Call <tt>define_attribute_methods</tt> passing each method you want to
-
# track.
-
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
-
# attribute.
-
#
-
# If you wish to also track previous changes on save or update, you need to
-
# add:
-
#
-
# @previously_changed = changes
-
#
-
# inside of your save or update method.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Dirty
-
#
-
# define_attribute_methods :name
-
#
-
# def name
-
# @name
-
# end
-
#
-
# def name=(val)
-
# name_will_change! unless val == @name
-
# @name = val
-
# end
-
#
-
# def save
-
# @previously_changed = changes
-
# @changed_attributes.clear
-
# end
-
# end
-
#
-
# A newly instantiated object is unchanged:
-
#
-
# person = Person.find_by_name('Uncle Bob')
-
# person.changed? # => false
-
#
-
# Change the name:
-
#
-
# person.name = 'Bob'
-
# person.changed? # => true
-
# person.name_changed? # => true
-
# person.name_was # => "Uncle Bob"
-
# person.name_change # => ["Uncle Bob", "Bob"]
-
# person.name = 'Bill'
-
# person.name_change # => ["Uncle Bob", "Bill"]
-
#
-
# Save the changes:
-
#
-
# person.save
-
# person.changed? # => false
-
# person.name_changed? # => false
-
#
-
# Assigning the same value leaves the attribute unchanged:
-
#
-
# person.name = 'Bill'
-
# person.name_changed? # => false
-
# person.name_change # => nil
-
#
-
# Which attributes have changed?
-
#
-
# person.name = 'Bob'
-
# person.changed # => ["name"]
-
# person.changes # => {"name" => ["Bill", "Bob"]}
-
#
-
# If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
-
# to mark that the attribute is changing. Otherwise ActiveModel can't track
-
# changes to in-place attributes.
-
#
-
# person.name_will_change!
-
# person.name_change # => ["Bill", "Bill"]
-
# person.name << 'y'
-
# person.name_change # => ["Bill", "Billy"]
-
1
module Dirty
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::AttributeMethods
-
-
1
included do
-
1
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
-
1
attribute_method_affix :prefix => 'reset_', :suffix => '!'
-
end
-
-
# Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
-
#
-
# person.changed? # => false
-
# person.name = 'bob'
-
# person.changed? # => true
-
1
def changed?
-
6
changed_attributes.present?
-
end
-
-
# Returns an array with the name of the attributes with unsaved changes.
-
#
-
# person.changed # => []
-
# person.name = 'bob'
-
# person.changed # => ["name"]
-
1
def changed
-
10
changed_attributes.keys
-
end
-
-
# Returns a hash of changed attributes indicating their original
-
# and new values like <tt>attr => [original value, new value]</tt>.
-
#
-
# person.changes # => {}
-
# person.name = 'bob'
-
# person.changes # => { "name" => ["bill", "bob"] }
-
1
def changes
-
15
HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
-
end
-
-
# Returns a hash of attributes that were changed before the model was saved.
-
#
-
# person.name # => "bob"
-
# person.name = 'robert'
-
# person.save
-
# person.previous_changes # => {"name" => ["bob", "robert"]}
-
1
def previous_changes
-
1
@previously_changed
-
end
-
-
# Returns a hash of the attributes with unsaved changes indicating their original
-
# values like <tt>attr => original value</tt>.
-
#
-
# person.name # => "bob"
-
# person.name = 'robert'
-
# person.changed_attributes # => {"name" => "bob"}
-
1
def changed_attributes
-
69
@changed_attributes ||= {}
-
end
-
-
1
private
-
-
# Handle <tt>*_changed?</tt> for +method_missing+.
-
1
def attribute_changed?(attr)
-
32
changed_attributes.include?(attr)
-
end
-
-
# Handle <tt>*_change</tt> for +method_missing+.
-
1
def attribute_change(attr)
-
8
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
-
end
-
-
# Handle <tt>*_was</tt> for +method_missing+.
-
1
def attribute_was(attr)
-
1
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
-
end
-
-
# Handle <tt>*_will_change!</tt> for +method_missing+.
-
1
def attribute_will_change!(attr)
-
13
return if attribute_changed?(attr)
-
-
11
begin
-
11
value = __send__(attr)
-
11
value = value.duplicable? ? value.clone : value
-
rescue TypeError, NoMethodError
-
end
-
-
11
changed_attributes[attr] = value
-
end
-
-
# Handle <tt>reset_*!</tt> for +method_missing+.
-
1
def reset_attribute!(attr)
-
1
__send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveModel
-
# == Active \Model \Errors
-
#
-
# Provides a modified +Hash+ that you can include in your object
-
# for handling error messages and interacting with Action Pack helpers.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
#
-
# # Required dependency for ActiveModel::Errors
-
# extend ActiveModel::Naming
-
#
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
#
-
# attr_accessor :name
-
# attr_reader :errors
-
#
-
# def validate!
-
# errors.add(:name, "can not be nil") if name == nil
-
# end
-
#
-
# # The following methods are needed to be minimally implemented
-
#
-
# def read_attribute_for_validation(attr)
-
# send(attr)
-
# end
-
#
-
# def Person.human_attribute_name(attr, options = {})
-
# attr
-
# end
-
#
-
# def Person.lookup_ancestors
-
# [self]
-
# end
-
#
-
# end
-
#
-
# The last three methods are required in your object for Errors to be
-
# able to generate error messages correctly and also handle multiple
-
# languages. Of course, if you extend your object with ActiveModel::Translation
-
# you will not need to implement the last two. Likewise, using
-
# ActiveModel::Validations will handle the validation related methods
-
# for you.
-
#
-
# The above allows you to do:
-
#
-
# p = Person.new
-
# person.validate! # => ["can not be nil"]
-
# person.errors.full_messages # => ["name can not be nil"]
-
# # etc..
-
1
class Errors
-
1
include Enumerable
-
-
1
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
-
-
1
attr_reader :messages
-
-
# Pass in the instance of the object that is using the errors object.
-
#
-
# class Person
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
# end
-
1
def initialize(base)
-
404
@base = base
-
404
@messages = {}
-
end
-
-
1
def initialize_dup(other) # :nodoc:
-
1
@messages = other.messages.dup
-
1
super
-
end
-
-
# Clear the error messages.
-
#
-
# person.errors.full_messages # => ["name can not be nil"]
-
# person.errors.clear
-
# person.errors.full_messages # => []
-
1
def clear
-
598
messages.clear
-
end
-
-
# Returns +true+ if the error messages include an error for the given key
-
# +attribute+, +false+ otherwise.
-
#
-
# person.errors.messages # => {:name=>["can not be nil"]}
-
# person.errors.include?(:name) # => true
-
# person.errors.include?(:age) # => false
-
1
def include?(attribute)
-
3
(v = messages[attribute]) && v.any?
-
end
-
# aliases include?
-
1
alias :has_key? :include?
-
-
# Get messages for +key+.
-
#
-
# person.errors.messages # => {:name=>["can not be nil"]}
-
# person.errors.get(:name) # => ["can not be nil"]
-
# person.errors.get(:age) # => nil
-
1
def get(key)
-
1213
messages[key]
-
end
-
-
# Set messages for +key+ to +value+.
-
#
-
# person.errors.get(:name) # => ["can not be nil"]
-
# person.errors.set(:name, ["can't be nil"])
-
# person.errors.get(:name) # => ["can't be nil"]
-
1
def set(key, value)
-
455
messages[key] = value
-
end
-
-
# Delete messages for +key+. Returns the deleted messages.
-
#
-
# person.errors.get(:name) # => ["can not be nil"]
-
# person.errors.delete(:name) # => ["can not be nil"]
-
# person.errors.get(:name) # => nil
-
1
def delete(key)
-
1
messages.delete(key)
-
end
-
-
# When passed a symbol or a name of a method, returns an array of errors
-
# for the method.
-
#
-
# person.errors[:name] # => ["can not be nil"]
-
# person.errors['name'] # => ["can not be nil"]
-
1
def [](attribute)
-
1213
get(attribute.to_sym) || set(attribute.to_sym, [])
-
end
-
-
# Adds to the supplied attribute the supplied error message.
-
#
-
# person.errors[:name] = "must be set"
-
# person.errors[:name] # => ['must be set']
-
1
def []=(attribute, error)
-
6
self[attribute] << error
-
end
-
-
# Iterates through each error key, value pair in the error messages hash.
-
# Yields the attribute and the error for that attribute. If the attribute
-
# has more than one error message, yields once for each error message.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.each do |attribute, error|
-
# # Will yield :name and "can't be blank"
-
# end
-
#
-
# person.errors.add(:name, "must be specified")
-
# person.errors.each do |attribute, error|
-
# # Will yield :name and "can't be blank"
-
# # then yield :name and "must be specified"
-
# end
-
1
def each
-
610
messages.each_key do |attribute|
-
813
self[attribute].each { |error| yield attribute, error }
-
end
-
end
-
-
# Returns the number of error messages.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.size # => 1
-
# person.errors.add(:name, "must be specified")
-
# person.errors.size # => 2
-
1
def size
-
3
values.flatten.size
-
end
-
-
# Returns all message values.
-
#
-
# person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
-
# person.errors.values # => [["can not be nil", "must be specified"]]
-
1
def values
-
3
messages.values
-
end
-
-
# Returns all message keys.
-
#
-
# person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
-
# person.errors.keys # => [:name]
-
1
def keys
-
4
messages.keys
-
end
-
-
# Returns an array of error messages, with the attribute name included.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.add(:name, "must be specified")
-
# person.errors.to_a # => ["name can't be blank", "name must be specified"]
-
1
def to_a
-
11
full_messages
-
end
-
-
# Returns the number of error messages.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.count # => 1
-
# person.errors.add(:name, "must be specified")
-
# person.errors.count # => 2
-
1
def count
-
5
to_a.size
-
end
-
-
# Returns +true+ if no errors are found, +false+ otherwise.
-
# If the error message is a string it can be empty.
-
#
-
# person.errors.full_messages # => ["name can not be nil"]
-
# person.errors.empty? # => false
-
1
def empty?
-
971
all? { |k, v| v && v.empty? && !v.is_a?(String) }
-
end
-
# aliases empty?
-
1
alias_method :blank?, :empty?
-
-
# Returns an xml formatted representation of the Errors hash.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.add(:name, "must be specified")
-
# person.errors.to_xml
-
# # =>
-
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
-
# # <errors>
-
# # <error>name can't be blank</error>
-
# # <error>name must be specified</error>
-
# # </errors>
-
1
def to_xml(options={})
-
1
to_a.to_xml({ :root => "errors", :skip_types => true }.merge!(options))
-
end
-
-
# Returns a Hash that can be used as the JSON representation for this
-
# object. You can pass the <tt>:full_messages</tt> option. This determines
-
# if the json object should contain full messages or not (false by default).
-
#
-
# person.as_json # => {:name=>["can not be nil"]}
-
# person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
-
1
def as_json(options=nil)
-
4
to_hash(options && options[:full_messages])
-
end
-
-
# Returns a Hash of attributes with their error messages. If +full_messages+
-
# is +true+, it will contain full messages (see +full_message+).
-
#
-
# person.to_hash # => {:name=>["can not be nil"]}
-
# person.to_hash(true) # => {:name=>["name can not be nil"]}
-
1
def to_hash(full_messages = false)
-
5
if full_messages
-
1
messages = {}
-
1
self.messages.each do |attribute, array|
-
5
messages[attribute] = array.map { |message| full_message(attribute, message) }
-
end
-
1
messages
-
else
-
4
self.messages.dup
-
end
-
end
-
-
# Adds +message+ to the error messages on +attribute+. More than one error
-
# can be added to the same +attribute+. If no +message+ is supplied,
-
# <tt>:invalid</tt> is assumed.
-
#
-
# person.errors.add(:name)
-
# # => ["is invalid"]
-
# person.errors.add(:name, 'must be implemented')
-
# # => ["is invalid", "must be implemented"]
-
#
-
# person.errors.messages
-
# # => {:name=>["must be implemented", "is invalid"]}
-
#
-
# If +message+ is a symbol, it will be translated using the appropriate
-
# scope (see +generate_message+).
-
#
-
# If +message+ is a proc, it will be called, allowing for things like
-
# <tt>Time.now</tt> to be used within an error.
-
#
-
# If the <tt>:strict</tt> option is set to true will raise
-
# ActiveModel::StrictValidationFailed instead of adding the error.
-
# <tt>:strict</tt> option can also be set to any other exception.
-
#
-
# person.errors.add(:name, nil, strict: true)
-
# # => ActiveModel::StrictValidationFailed: name is invalid
-
# person.errors.add(:name, nil, strict: NameIsInvalid)
-
# # => NameIsInvalid: name is invalid
-
#
-
# person.errors.messages # => {}
-
1
def add(attribute, message = nil, options = {})
-
423
message = normalize_message(attribute, message, options)
-
423
if exception = options[:strict]
-
6
exception = ActiveModel::StrictValidationFailed if exception == true
-
6
raise exception, full_message(attribute, message)
-
end
-
-
417
self[attribute] << message
-
end
-
-
# Will add an error message to each of the attributes in +attributes+
-
# that is empty.
-
#
-
# person.errors.add_on_empty(:name)
-
# person.errors.messages
-
# # => {:name=>["can't be empty"]}
-
1
def add_on_empty(attributes, options = {})
-
3
Array(attributes).each do |attribute|
-
4
value = @base.send(:read_attribute_for_validation, attribute)
-
4
is_empty = value.respond_to?(:empty?) ? value.empty? : false
-
4
add(attribute, :empty, options) if value.nil? || is_empty
-
end
-
end
-
-
# Will add an error message to each of the attributes in +attributes+ that
-
# is blank (using Object#blank?).
-
#
-
# person.errors.add_on_blank(:name)
-
# person.errors.messages
-
# # => {:name=>["can't be blank"]}
-
1
def add_on_blank(attributes, options = {})
-
58
Array(attributes).each do |attribute|
-
65
value = @base.send(:read_attribute_for_validation, attribute)
-
65
add(attribute, :blank, options) if value.blank?
-
end
-
end
-
-
# Returns +true+ if an error on the attribute with the given message is
-
# present, +false+ otherwise. +message+ is treated the same as for +add+.
-
#
-
# person.errors.add :name, :blank
-
# person.errors.added? :name, :blank # => true
-
1
def added?(attribute, message = nil, options = {})
-
7
message = normalize_message(attribute, message, options)
-
7
self[attribute].include? message
-
end
-
-
# Returns all the full error messages in an array.
-
#
-
# class Person
-
# validates_presence_of :name, :address, :email
-
# validates_length_of :name, in: 5..30
-
# end
-
#
-
# person = Person.create(address: '123 First St.')
-
# person.errors.full_messages
-
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
-
1
def full_messages
-
42
map { |attribute, message| full_message(attribute, message) }
-
end
-
-
# Returns a full message for a given attribute.
-
#
-
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
-
1
def full_message(attribute, message)
-
37
return message if attribute == :base
-
32
attr_name = attribute.to_s.tr('.', '_').humanize
-
32
attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
-
32
I18n.t(:"errors.format", {
-
:default => "%{attribute} %{message}",
-
:attribute => attr_name,
-
:message => message
-
})
-
end
-
-
# Translates an error message in its default scope
-
# (<tt>activemodel.errors.messages</tt>).
-
#
-
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
-
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
-
# that is not there also, it returns the translation of the default message
-
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
-
# name, translated attribute name and the value are available for
-
# interpolation.
-
#
-
# When using inheritance in your models, it will check all the inherited
-
# models too, but only if the model itself hasn't been found. Say you have
-
# <tt>class Admin < User; end</tt> and you wanted the translation for
-
# the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
-
# it looks for these translations:
-
#
-
# * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.admin.blank</tt>
-
# * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.user.blank</tt>
-
# * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
-
# * <tt>activemodel.errors.messages.blank</tt>
-
# * <tt>errors.attributes.title.blank</tt>
-
# * <tt>errors.messages.blank</tt>
-
1
def generate_message(attribute, type = :invalid, options = {})
-
334
type = options.delete(:message) if options[:message].is_a?(Symbol)
-
-
334
if @base.class.respond_to?(:i18n_scope)
-
326
defaults = @base.class.lookup_ancestors.map do |klass|
-
328
[ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
-
:"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
-
end
-
else
-
8
defaults = []
-
end
-
-
334
defaults << options.delete(:message)
-
334
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
-
334
defaults << :"errors.attributes.#{attribute}.#{type}"
-
334
defaults << :"errors.messages.#{type}"
-
-
334
defaults.compact!
-
334
defaults.flatten!
-
-
334
key = defaults.shift
-
334
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
-
-
334
options = {
-
:default => defaults,
-
:model => @base.class.model_name.human,
-
:attribute => @base.class.human_attribute_name(attribute),
-
:value => value
-
}.merge!(options)
-
-
334
I18n.translate(key, options)
-
end
-
-
1
private
-
1
def normalize_message(attribute, message, options)
-
430
message ||= :invalid
-
-
430
case message
-
when Symbol
-
386
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
-
when Proc
-
3
message.call
-
else
-
41
message
-
end
-
end
-
end
-
-
# Raised when a validation cannot be corrected by end users and are considered
-
# exceptional.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
#
-
# validates_presence_of :name, strict: true
-
# end
-
#
-
# person = Person.new
-
# person.name = nil
-
# person.valid?
-
# # => ActiveModel::StrictValidationFailed: Name can't be blank
-
1
class StrictValidationFailed < StandardError
-
end
-
end
-
1
module ActiveModel
-
# Raised when forbidden attributes are used for mass assignment.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Bob')
-
# Person.new(params)
-
# # => ActiveModel::ForbiddenAttributesError
-
#
-
# params.permit!
-
# Person.new(params)
-
# # => #<Person id: nil, name: "Bob">
-
1
class ForbiddenAttributesError < StandardError
-
end
-
-
1
module ForbiddenAttributesProtection # :nodoc:
-
1
protected
-
1
def sanitize_for_mass_assignment(attributes)
-
3
if attributes.respond_to?(:permitted?) && !attributes.permitted?
-
1
raise ActiveModel::ForbiddenAttributesError
-
else
-
2
attributes
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module Lint
-
# == Active \Model \Lint \Tests
-
#
-
# You can test whether an object is compliant with the Active \Model API by
-
# including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will
-
# include tests that tell you whether your object is fully compliant,
-
# or if not, which aspects of the API are not implemented.
-
#
-
# Note an object is not required to implement all APIs in order to work
-
# with Action Pack. This module only intends to provide guidance in case
-
# you want all features out of the box.
-
#
-
# These tests do not attempt to determine the semantic correctness of the
-
# returned values. For instance, you could implement <tt>valid?</tt> to
-
# always return true, and the tests would pass. It is up to you to ensure
-
# that the values are semantically meaningful.
-
#
-
# Objects you pass in are expected to return a compliant object from a call
-
# to <tt>to_model</tt>. It is perfectly fine for <tt>to_model</tt> to return
-
# +self+.
-
1
module Tests
-
-
# == Responds to <tt>to_key</tt>
-
#
-
# Returns an Enumerable of all (primary) key attributes
-
# or nil if <tt>model.persisted?</tt> is false. This is used by
-
# <tt>dom_id</tt> to generate unique ids for the object.
-
1
def test_to_key
-
2
assert model.respond_to?(:to_key), "The model should respond to to_key"
-
2
def model.persisted?() false end
-
2
assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
-
end
-
-
# == Responds to <tt>to_param</tt>
-
#
-
# Returns a string representing the object's key suitable for use in URLs
-
# or +nil+ if <tt>model.persisted?</tt> is +false+.
-
#
-
# Implementers can decide to either raise an exception or provide a
-
# default in case the record uses a composite primary key. There are no
-
# tests for this behavior in lint because it doesn't make sense to force
-
# any of the possible implementation strategies on the implementer.
-
# However, if the resource is not persisted?, then <tt>to_param</tt>
-
# should always return +nil+.
-
1
def test_to_param
-
2
assert model.respond_to?(:to_param), "The model should respond to to_param"
-
2
def model.to_key() [1] end
-
4
def model.persisted?() false end
-
2
assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
-
end
-
-
# == Responds to <tt>to_partial_path</tt>
-
#
-
# Returns a string giving a relative path. This is used for looking up
-
# partials. For example, a BlogPost model might return "blog_posts/blog_post"
-
1
def test_to_partial_path
-
2
assert model.respond_to?(:to_partial_path), "The model should respond to to_partial_path"
-
2
assert_kind_of String, model.to_partial_path
-
end
-
-
# == Responds to <tt>persisted?</tt>
-
#
-
# Returns a boolean that specifies whether the object has been persisted
-
# yet. This is used when calculating the URL for an object. If the object
-
# is not persisted, a form for that object, for instance, will route to
-
# the create action. If it is persisted, a form for the object will routes
-
# to the update action.
-
1
def test_persisted?
-
2
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
-
2
assert_boolean model.persisted?, "persisted?"
-
end
-
-
# == \Naming
-
#
-
# Model.model_name must return a string with some convenience methods:
-
# <tt>:human</tt>, <tt>:singular</tt> and <tt>:plural</tt>. Check
-
# ActiveModel::Naming for more information.
-
1
def test_model_naming
-
2
assert model.class.respond_to?(:model_name), "The model should respond to model_name"
-
2
model_name = model.class.model_name
-
2
assert model_name.respond_to?(:to_str)
-
2
assert model_name.human.respond_to?(:to_str)
-
2
assert model_name.singular.respond_to?(:to_str)
-
2
assert model_name.plural.respond_to?(:to_str)
-
end
-
-
# == \Errors Testing
-
#
-
# Returns an object that implements [](attribute) defined which returns an
-
# Array of Strings that are the errors for the attribute in question.
-
# If localization is used, the Strings should be localized for the current
-
# locale. If no error is present, this method should return an empty Array.
-
1
def test_errors_aref
-
2
assert model.respond_to?(:errors), "The model should respond to errors"
-
2
assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
-
end
-
-
1
private
-
1
def model
-
30
assert @model.respond_to?(:to_model), "The object should respond_to to_model"
-
30
@model.to_model
-
end
-
-
1
def assert_boolean(result, name)
-
2
assert result == true || result == false, "#{name} should be a boolean"
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active \Model Basic \Model
-
#
-
# Includes the required interface for an object to interact with
-
# <tt>ActionPack</tt>, using different <tt>ActiveModel</tt> modules.
-
# It includes model name introspections, conversions, translations and
-
# validations. Besides that, it allows you to initialize the object with a
-
# hash of attributes, pretty much like <tt>ActiveRecord</tt> does.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Model
-
# attr_accessor :name, :age
-
# end
-
#
-
# person = Person.new(name: 'bob', age: '18')
-
# person.name # => 'bob'
-
# person.age # => 18
-
#
-
# Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
-
# to return +false+, which is the most common case. You may want to override
-
# it in your class to simulate a different scenario:
-
#
-
# class Person
-
# include ActiveModel::Model
-
# attr_accessor :id, :name
-
#
-
# def persisted?
-
# self.id == 1
-
# end
-
# end
-
#
-
# person = Person.new(id: 1, name: 'bob')
-
# person.persisted? # => true
-
#
-
# Also, if for some reason you need to run code on <tt>initialize</tt>, make
-
# sure you call +super+ if you want the attributes hash initialization to
-
# happen.
-
#
-
# class Person
-
# include ActiveModel::Model
-
# attr_accessor :id, :name, :omg
-
#
-
# def initialize(attributes={})
-
# super
-
# @omg ||= true
-
# end
-
# end
-
#
-
# person = Person.new(id: 1, name: 'bob')
-
# person.omg # => true
-
#
-
# For more detailed information on other functionalities available, please
-
# refer to the specific modules included in <tt>ActiveModel::Model</tt>
-
# (see below).
-
1
module Model
-
1
def self.included(base) #:nodoc:
-
1
base.class_eval do
-
1
extend ActiveModel::Naming
-
1
extend ActiveModel::Translation
-
1
include ActiveModel::Validations
-
1
include ActiveModel::Conversion
-
end
-
end
-
-
# Initializes a new model with the given +params+.
-
#
-
# class Person
-
# include ActiveModel::Model
-
# attr_accessor :name, :age
-
# end
-
#
-
# person = Person.new(name: 'bob', age: '18')
-
# person.name # => "bob"
-
# person.age # => 18
-
1
def initialize(params={})
-
11
params.each do |attr, value|
-
1
self.public_send("#{attr}=", value)
-
11
end if params
-
end
-
-
# Indicates if the model is persisted. Default is +false+.
-
#
-
# class Person
-
# include ActiveModel::Model
-
# attr_accessor :id, :name
-
# end
-
#
-
# person = Person.new(id: 1, name: 'bob')
-
# person.persisted? # => false
-
1
def persisted?
-
1
false
-
end
-
end
-
end
-
1
require 'active_support/inflector'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/introspection'
-
-
1
module ActiveModel
-
1
class Name
-
1
include Comparable
-
-
1
attr_reader :singular, :plural, :element, :collection,
-
:singular_route_key, :route_key, :param_key, :i18n_key,
-
:name
-
-
1
alias_method :cache_key, :collection
-
-
##
-
# :method: ==
-
#
-
# :call-seq:
-
# ==(other)
-
#
-
# Equivalent to <tt>String#==</tt>. Returns +true+ if the class name and
-
# +other+ are equal, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name == 'BlogPost' # => true
-
# BlogPost.model_name == 'Blog Post' # => false
-
-
##
-
# :method: ===
-
#
-
# :call-seq:
-
# ===(other)
-
#
-
# Equivalent to <tt>#==</tt>.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name === 'BlogPost' # => true
-
# BlogPost.model_name === 'Blog Post' # => false
-
-
##
-
# :method: <=>
-
#
-
# :call-seq:
-
# ==(other)
-
#
-
# Equivalent to <tt>String#<=></tt>.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name <=> 'BlogPost' # => 0
-
# BlogPost.model_name <=> 'Blog' # => 1
-
# BlogPost.model_name <=> 'BlogPosts' # => -1
-
-
##
-
# :method: =~
-
#
-
# :call-seq:
-
# =~(regexp)
-
#
-
# Equivalent to <tt>String#=~</tt>. Match the class name against the given
-
# regexp. Returns the position where the match starts or +nil+ if there is
-
# no match.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name =~ /Post/ # => 4
-
# BlogPost.model_name =~ /\d/ # => nil
-
-
##
-
# :method: !~
-
#
-
# :call-seq:
-
# !~(regexp)
-
#
-
# Equivalent to <tt>String#!~</tt>. Match the class name against the given
-
# regexp. Returns +true+ if there is no match, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name !~ /Post/ # => false
-
# BlogPost.model_name !~ /\d/ # => true
-
-
##
-
# :method: eql?
-
#
-
# :call-seq:
-
# eql?(other)
-
#
-
# Equivalent to <tt>String#eql?</tt>. Returns +true+ if the class name and
-
# +other+ have the same length and content, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.eql?('BlogPost') # => true
-
# BlogPost.model_name.eql?('Blog Post') # => false
-
-
##
-
# :method: to_s
-
#
-
# :call-seq:
-
# to_s()
-
#
-
# Returns the class name.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.to_s # => "BlogPost"
-
-
##
-
# :method: to_str
-
#
-
# :call-seq:
-
# to_str()
-
#
-
# Equivalent to +to_s+.
-
1
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
-
:to_str, :to => :name
-
-
# Returns a new ActiveModel::Name instance. By default, the +namespace+
-
# and +name+ option will take the namespace and name of the given class
-
# respectively.
-
#
-
# module Foo
-
# class Bar
-
# end
-
# end
-
#
-
# ActiveModel::Name.new(Foo::Bar).to_s
-
# # => "Foo::Bar"
-
1
def initialize(klass, namespace = nil, name = nil)
-
52
@name = name || klass.name
-
-
52
raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
-
-
51
@unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
-
51
@klass = klass
-
51
@singular = _singularize(@name)
-
51
@plural = ActiveSupport::Inflector.pluralize(@singular)
-
51
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
-
51
@human = ActiveSupport::Inflector.humanize(@element)
-
51
@collection = ActiveSupport::Inflector.tableize(@name)
-
51
@param_key = (namespace ? _singularize(@unnamespaced) : @singular)
-
51
@i18n_key = @name.underscore.to_sym
-
-
51
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
-
51
@singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
-
51
@route_key << "_index" if @plural == @singular
-
end
-
-
# Transform the model name into a more humane format, using I18n. By default,
-
# it will underscore then humanize the class name.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.human # => "Blog post"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human(options={})
-
return @human unless @klass.respond_to?(:lookup_ancestors) &&
-
345
@klass.respond_to?(:i18n_scope)
-
-
331
defaults = @klass.lookup_ancestors.map do |klass|
-
335
klass.model_name.i18n_key
-
end
-
-
331
defaults << options[:default] if options[:default]
-
331
defaults << @human
-
-
331
options = { :scope => [@klass.i18n_scope, :models], :count => 1, :default => defaults }.merge!(options.except(:default))
-
331
I18n.translate(defaults.shift, options)
-
end
-
-
1
private
-
-
1
def _singularize(string, replacement='_')
-
60
ActiveSupport::Inflector.underscore(string).tr('/', replacement)
-
end
-
end
-
-
# == Active \Model \Naming
-
#
-
# Creates a +model_name+ method on your object.
-
#
-
# To implement, just extend ActiveModel::Naming in your object:
-
#
-
# class BookCover
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BookCover.model_name # => "BookCover"
-
# BookCover.model_name.human # => "Book cover"
-
#
-
# BookCover.model_name.i18n_key # => :book_cover
-
# BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
-
#
-
# Providing the functionality that ActiveModel::Naming provides in your object
-
# is required to pass the Active Model Lint test. So either extending the
-
# provided method below, or rolling your own is required.
-
1
module Naming
-
# Returns an ActiveModel::Name object for module. It can be
-
# used to retrieve all kinds of naming-related information
-
# (See ActiveModel::Name for more information).
-
#
-
# class Person < ActiveModel::Model
-
# end
-
#
-
# Person.model_name # => Person
-
# Person.model_name.class # => ActiveModel::Name
-
# Person.model_name.singular # => "person"
-
# Person.model_name.plural # => "people"
-
1
def model_name
-
@_model_name ||= begin
-
20
namespace = self.parents.detect do |n|
-
26
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
-
end
-
20
ActiveModel::Name.new(self, namespace)
-
1782
end
-
end
-
-
# Returns the plural class name of a record or class.
-
#
-
# ActiveModel::Naming.plural(post) # => "posts"
-
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
-
1
def self.plural(record_or_class)
-
5
model_name_from_record_or_class(record_or_class).plural
-
end
-
-
# Returns the singular class name of a record or class.
-
#
-
# ActiveModel::Naming.singular(post) # => "post"
-
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
-
1
def self.singular(record_or_class)
-
4
model_name_from_record_or_class(record_or_class).singular
-
end
-
-
# Identifies whether the class name of a record or class is uncountable.
-
#
-
# ActiveModel::Naming.uncountable?(Sheep) # => true
-
# ActiveModel::Naming.uncountable?(Post) # => false
-
1
def self.uncountable?(record_or_class)
-
2
plural(record_or_class) == singular(record_or_class)
-
end
-
-
# Returns string to use while generating route names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.singular_route_key(Blog::Post) #=> post
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.singular_route_key(Blog::Post) #=> blog_post
-
1
def self.singular_route_key(record_or_class)
-
3
model_name_from_record_or_class(record_or_class).singular_route_key
-
end
-
-
# Returns string to use while generating route names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.route_key(Blog::Post) #=> posts
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.route_key(Blog::Post) #=> blog_posts
-
#
-
# The route key also considers if the noun is uncountable and, in
-
# such cases, automatically appends _index.
-
1
def self.route_key(record_or_class)
-
3
model_name_from_record_or_class(record_or_class).route_key
-
end
-
-
# Returns string to use for params names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.param_key(Blog::Post) #=> post
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.param_key(Blog::Post) #=> blog_post
-
1
def self.param_key(record_or_class)
-
2
model_name_from_record_or_class(record_or_class).param_key
-
end
-
-
1
def self.model_name_from_record_or_class(record_or_class) #:nodoc:
-
17
if record_or_class.respond_to?(:model_name)
-
11
record_or_class.model_name
-
6
elsif record_or_class.respond_to?(:to_model)
-
6
record_or_class.to_model.class.model_name
-
else
-
record_or_class.class.model_name
-
end
-
end
-
1
private_class_method :model_name_from_record_or_class
-
end
-
end
-
1
require 'set'
-
-
1
module ActiveModel
-
# Stores the enabled/disabled state of individual observers for
-
# a particular model class.
-
1
class ObserverArray < Array
-
1
attr_reader :model_class
-
1
def initialize(model_class, *args) #:nodoc:
-
5
@model_class = model_class
-
5
super(*args)
-
end
-
-
# Returns +true+ if the given observer is disabled for the model class,
-
# +false+ otherwise.
-
1
def disabled_for?(observer) #:nodoc:
-
157
disabled_observers.include?(observer.class)
-
end
-
-
# Disables one or more observers. This supports multiple forms:
-
#
-
# ORM.observers.disable :all
-
# # => disables all observers for all models subclassed from
-
# # an ORM base class that includes ActiveModel::Observing
-
# # e.g. ActiveRecord::Base
-
#
-
# ORM.observers.disable :user_observer
-
# # => disables the UserObserver
-
#
-
# User.observers.disable AuditTrail
-
# # => disables the AuditTrail observer for User notifications.
-
# # Other models will still notify the AuditTrail observer.
-
#
-
# ORM.observers.disable :observer_1, :observer_2
-
# # => disables Observer1 and Observer2 for all models.
-
#
-
# User.observers.disable :all do
-
# # all user observers are disabled for
-
# # just the duration of the block
-
# end
-
1
def disable(*observers, &block)
-
17
set_enablement(false, observers, &block)
-
end
-
-
# Enables one or more observers. This supports multiple forms:
-
#
-
# ORM.observers.enable :all
-
# # => enables all observers for all models subclassed from
-
# # an ORM base class that includes ActiveModel::Observing
-
# # e.g. ActiveRecord::Base
-
#
-
# ORM.observers.enable :user_observer
-
# # => enables the UserObserver
-
#
-
# User.observers.enable AuditTrail
-
# # => enables the AuditTrail observer for User notifications.
-
# # Other models will not be affected (i.e. they will not
-
# # trigger notifications to AuditTrail if previously disabled)
-
#
-
# ORM.observers.enable :observer_1, :observer_2
-
# # => enables Observer1 and Observer2 for all models.
-
#
-
# User.observers.enable :all do
-
# # all user observers are enabled for
-
# # just the duration of the block
-
# end
-
#
-
# Note: all observers are enabled by default. This method is only
-
# useful when you have previously disabled one or more observers.
-
1
def enable(*observers, &block)
-
62
set_enablement(true, observers, &block)
-
end
-
-
1
protected
-
-
1
def disabled_observers #:nodoc:
-
781
@disabled_observers ||= Set.new
-
end
-
-
1
def observer_class_for(observer) #:nodoc:
-
621
return observer if observer.is_a?(Class)
-
-
27
if observer.respond_to?(:to_sym) # string/symbol
-
27
observer.to_s.camelize.constantize
-
else
-
raise ArgumentError, "#{observer} was not a class or a " +
-
"lowercase, underscored class name as expected."
-
end
-
end
-
-
1
def start_transaction #:nodoc:
-
7
disabled_observer_stack.push(disabled_observers.dup)
-
7
each_subclass_array do |array|
-
4
array.start_transaction
-
end
-
end
-
-
1
def disabled_observer_stack #:nodoc:
-
14
@disabled_observer_stack ||= []
-
end
-
-
1
def end_transaction #:nodoc:
-
7
@disabled_observers = disabled_observer_stack.pop
-
7
each_subclass_array do |array|
-
4
array.end_transaction
-
end
-
end
-
-
1
def transaction #:nodoc:
-
3
start_transaction
-
-
3
begin
-
3
yield
-
ensure
-
3
end_transaction
-
end
-
end
-
-
1
def each_subclass_array #:nodoc:
-
157
model_class.descendants.each do |subclass|
-
76
yield subclass.observers
-
end
-
end
-
-
1
def set_enablement(enabled, observers) #:nodoc:
-
150
if block_given?
-
3
transaction do
-
3
set_enablement(enabled, observers)
-
3
yield
-
end
-
else
-
147
observers = ActiveModel::Observer.descendants if observers == [:all]
-
147
observers.each do |obs|
-
621
klass = observer_class_for(obs)
-
-
621
unless klass < ActiveModel::Observer
-
4
raise ArgumentError.new("#{obs} does not refer to a valid observer")
-
end
-
-
617
if enabled
-
489
disabled_observers.delete(klass)
-
else
-
128
disabled_observers << klass
-
end
-
end
-
-
143
each_subclass_array do |array|
-
68
array.set_enablement(enabled, observers)
-
end
-
end
-
end
-
end
-
end
-
1
require 'singleton'
-
1
require 'active_model/observer_array'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/descendants_tracker'
-
-
1
module ActiveModel
-
# == Active \Model Observers Activation
-
1
module Observing
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
3
extend ActiveSupport::DescendantsTracker
-
end
-
-
1
module ClassMethods
-
# Activates the observers assigned.
-
#
-
# class ORM
-
# include ActiveModel::Observing
-
# end
-
#
-
# # Calls PersonObserver.instance
-
# ORM.observers = :person_observer
-
#
-
# # Calls Cacher.instance and GarbageCollector.instance
-
# ORM.observers = :cacher, :garbage_collector
-
#
-
# # Same as above, just using explicit class references
-
# ORM.observers = Cacher, GarbageCollector
-
#
-
# Note: Setting this does not instantiate the observers yet.
-
# <tt>instantiate_observers</tt> is called during startup, and before
-
# each development request.
-
1
def observers=(*values)
-
16
observers.replace(values.flatten)
-
end
-
-
# Gets an array of observers observing this model. The array also provides
-
# +enable+ and +disable+ methods that allow you to selectively enable and
-
# disable observers (see ActiveModel::ObserverArray.enable and
-
# ActiveModel::ObserverArray.disable for more on this).
-
#
-
# class ORM
-
# include ActiveModel::Observing
-
# end
-
#
-
# ORM.observers = :cacher, :garbage_collector
-
# ORM.observers # => [:cacher, :garbage_collector]
-
# ORM.observers.class # => ActiveModel::ObserverArray
-
1
def observers
-
357
@observers ||= ObserverArray.new(self)
-
end
-
-
# Returns the current observer instances.
-
#
-
# class Foo
-
# include ActiveModel::Observing
-
#
-
# attr_accessor :status
-
# end
-
#
-
# class FooObserver < ActiveModel::Observer
-
# def on_spec(record, *args)
-
# record.status = true
-
# end
-
# end
-
#
-
# Foo.observers = FooObserver
-
# Foo.instantiate_observers
-
#
-
# Foo.observer_instances # => [#<FooObserver:0x007fc212c40820>]
-
1
def observer_instances
-
96
@observer_instances ||= []
-
end
-
-
# Instantiate the global observers.
-
#
-
# class Foo
-
# include ActiveModel::Observing
-
#
-
# attr_accessor :status
-
# end
-
#
-
# class FooObserver < ActiveModel::Observer
-
# def on_spec(record, *args)
-
# record.status = true
-
# end
-
# end
-
#
-
# Foo.observers = FooObserver
-
#
-
# foo = Foo.new
-
# foo.status = false
-
# foo.notify_observers(:on_spec)
-
# foo.status # => false
-
#
-
# Foo.instantiate_observers # => [FooObserver]
-
#
-
# foo = Foo.new
-
# foo.status = false
-
# foo.notify_observers(:on_spec)
-
# foo.status # => true
-
1
def instantiate_observers
-
13
observers.each { |o| instantiate_observer(o) }
-
end
-
-
# Add a new observer to the pool. The new observer needs to respond to
-
# <tt>update</tt>, otherwise it raises an +ArgumentError+ exception.
-
#
-
# class Foo
-
# include ActiveModel::Observing
-
# end
-
#
-
# class FooObserver < ActiveModel::Observer
-
# end
-
#
-
# Foo.add_observer(FooObserver.instance)
-
#
-
# Foo.observers_instance
-
# # => [#<FooObserver:0x007fccf55d9390>]
-
1
def add_observer(observer)
-
11
unless observer.respond_to? :update
-
raise ArgumentError, "observer needs to respond to 'update'"
-
end
-
11
observer_instances << observer
-
end
-
-
# Fires notifications to model's observers.
-
#
-
# def save
-
# notify_observers(:before_save)
-
# ...
-
# notify_observers(:after_save)
-
# end
-
#
-
# Custom notifications can be sent in a similar fashion:
-
#
-
# notify_observers(:custom_notification, :foo)
-
#
-
# This will call <tt>custom_notification</tt>, passing as arguments
-
# the current object and <tt>:foo</tt>.
-
1
def notify_observers(*args)
-
243
observer_instances.each { |observer| observer.update(*args) }
-
end
-
-
# Returns the total number of instantiated observers.
-
#
-
# class Foo
-
# include ActiveModel::Observing
-
#
-
# attr_accessor :status
-
# end
-
#
-
# class FooObserver < ActiveModel::Observer
-
# def on_spec(record, *args)
-
# record.status = true
-
# end
-
# end
-
#
-
# Foo.observers = FooObserver
-
# Foo.observers_count # => 0
-
# Foo.instantiate_observers
-
# Foo.observers_count # => 1
-
1
def observers_count
-
2
observer_instances.size
-
end
-
-
# <tt>count_observers</tt> is deprecated. Use #observers_count.
-
1
def count_observers
-
msg = "count_observers is deprecated in favor of observers_count"
-
ActiveSupport::Deprecation.warn msg
-
observers_count
-
end
-
-
1
protected
-
1
def instantiate_observer(observer) #:nodoc:
-
# string/symbol
-
6
if observer.respond_to?(:to_sym)
-
4
observer = observer.to_s.camelize.constantize
-
end
-
6
if observer.respond_to?(:instance)
-
3
observer.instance
-
else
-
3
raise ArgumentError,
-
"#{observer} must be a lowercase, underscored class name (or " +
-
"the class itself) responding to the method :instance. " +
-
"Example: Person.observers = :big_brother # calls " +
-
"BigBrother.instance"
-
end
-
end
-
-
# Notify observers when the observed class is subclassed.
-
1
def inherited(subclass) #:nodoc:
-
3
super
-
3
notify_observers :observed_class_inherited, subclass
-
end
-
end
-
-
# Notify a change to the list of observers.
-
#
-
# class Foo
-
# include ActiveModel::Observing
-
#
-
# attr_accessor :status
-
# end
-
#
-
# class FooObserver < ActiveModel::Observer
-
# def on_spec(record, *args)
-
# record.status = true
-
# end
-
# end
-
#
-
# Foo.observers = FooObserver
-
# Foo.instantiate_observers # => [FooObserver]
-
#
-
# foo = Foo.new
-
# foo.status = false
-
# foo.notify_observers(:on_spec)
-
# foo.status # => true
-
#
-
# See ActiveModel::Observing::ClassMethods.notify_observers for more
-
# information.
-
1
def notify_observers(method, *extra_args)
-
77
self.class.notify_observers(method, self, *extra_args)
-
end
-
end
-
-
# == Active \Model Observers
-
#
-
# Observer classes respond to life cycle callbacks to implement trigger-like
-
# behavior outside the original class. This is a great way to reduce the
-
# clutter that normally comes when the model class is burdened with
-
# functionality that doesn't pertain to the core responsibility of the
-
# class.
-
#
-
# class CommentObserver < ActiveModel::Observer
-
# def after_save(comment)
-
# Notifications.comment('admin@do.com', 'New comment was posted', comment).deliver
-
# end
-
# end
-
#
-
# This Observer sends an email when a <tt>Comment#save</tt> is finished.
-
#
-
# class ContactObserver < ActiveModel::Observer
-
# def after_create(contact)
-
# contact.logger.info('New contact added!')
-
# end
-
#
-
# def after_destroy(contact)
-
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
-
# end
-
# end
-
#
-
# This Observer uses logger to log when specific callbacks are triggered.
-
#
-
# == \Observing a class that can't be inferred
-
#
-
# Observers will by default be mapped to the class with which they share a
-
# name. So <tt>CommentObserver</tt> will be tied to observing <tt>Comment</tt>,
-
# <tt>ProductManagerObserver</tt> to <tt>ProductManager</tt>, and so on. If
-
# you want to name your observer differently than the class you're interested
-
# in observing, you can use the <tt>Observer.observe</tt> class method which
-
# takes either the concrete class (<tt>Product</tt>) or a symbol for that
-
# class (<tt>:product</tt>):
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# observe :account
-
#
-
# def after_update(account)
-
# AuditTrail.new(account, 'UPDATED')
-
# end
-
# end
-
#
-
# If the audit observer needs to watch more than one kind of object, this can
-
# be specified with multiple arguments:
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# observe :account, :balance
-
#
-
# def after_update(record)
-
# AuditTrail.new(record, 'UPDATED')
-
# end
-
# end
-
#
-
# The <tt>AuditObserver</tt> will now act on both updates to <tt>Account</tt>
-
# and <tt>Balance</tt> by treating them both as records.
-
#
-
# If you're using an Observer in a Rails application with Active Record, be
-
# sure to read about the necessary configuration in the documentation for
-
# ActiveRecord::Observer.
-
1
class Observer
-
1
include Singleton
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
class << self
-
# Attaches the observer to the supplied model classes.
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# observe :account, :balance
-
# end
-
#
-
# AuditObserver.observed_classes # => [Account, Balance]
-
1
def observe(*models)
-
8
models.flatten!
-
20
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
-
18
singleton_class.redefine_method(:observed_classes) { models }
-
end
-
-
# Returns an array of Classes to observe.
-
#
-
# AccountObserver.observed_classes # => [Account]
-
#
-
# You can override this instead of using the +observe+ helper.
-
#
-
# class AuditObserver < ActiveModel::Observer
-
# def self.observed_classes
-
# [Account, Balance]
-
# end
-
# end
-
1
def observed_classes
-
6
Array(observed_class)
-
end
-
-
# Returns the class observed by default. It's inferred from the observer's
-
# class name.
-
#
-
# PersonObserver.observed_class # => Person
-
# AccountObserver.observed_class # => Account
-
1
def observed_class
-
7
name[/(.*)Observer/, 1].try :constantize
-
end
-
end
-
-
# Start observing the declared classes and their subclasses.
-
# Called automatically by the instance method.
-
1
def initialize #:nodoc:
-
17
observed_classes.each { |klass| add_observer!(klass) }
-
end
-
-
1
def observed_classes #:nodoc:
-
14
self.class.observed_classes
-
end
-
-
# Send observed_method(object) if the method exists and
-
# the observer is enabled for the given object's class.
-
1
def update(observed_method, object, *extra_args, &block) #:nodoc:
-
161
return if !respond_to?(observed_method) || disabled_for?(object)
-
107
send(observed_method, object, *extra_args, &block)
-
end
-
-
# Special method sent by the observed class when it is inherited.
-
# Passes the new subclass.
-
1
def observed_class_inherited(subclass) #:nodoc:
-
2
self.class.observe(observed_classes + [subclass])
-
2
add_observer!(subclass)
-
end
-
-
1
protected
-
1
def add_observer!(klass) #:nodoc:
-
11
klass.add_observer(self)
-
end
-
-
# Returns true if notifications are disabled for this object.
-
1
def disabled_for?(object) #:nodoc:
-
159
klass = object.class
-
159
return false unless klass.respond_to?(:observers)
-
157
klass.observers.disabled_for?(self)
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module SecurePassword
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Adds methods to set and authenticate against a BCrypt password.
-
# This mechanism requires you to have a password_digest attribute.
-
#
-
# Validations for presence of password on create, confirmation of password
-
# (using a +password_confirmation+ attribute) are automatically added. If
-
# you wish to turn off validations, pass <tt>validations: false</tt> as an
-
# argument. You can add more validations by hand if need be.
-
#
-
# You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
-
#
-
# gem 'bcrypt-ruby', '~> 3.0.0'
-
#
-
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
-
#
-
# # Schema: User(name:string, password_digest:string)
-
# class User < ActiveRecord::Base
-
# has_secure_password
-
# end
-
#
-
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
-
# user.save # => false, password required
-
# user.password = 'mUc3m00RsqyRe'
-
# user.save # => false, confirmation doesn't match
-
# user.password_confirmation = 'mUc3m00RsqyRe'
-
# user.save # => true
-
# user.authenticate('notright') # => false
-
# user.authenticate('mUc3m00RsqyRe') # => user
-
# User.find_by_name('david').try(:authenticate, 'notright') # => false
-
# User.find_by_name('david').try(:authenticate, 'mUc3m00RsqyRe') # => user
-
1
def has_secure_password(options = {})
-
# Load bcrypt-ruby only when has_secure_password is used.
-
# This is to avoid ActiveModel (and by extension the entire framework)
-
# being dependent on a binary library.
-
4
gem 'bcrypt-ruby', '~> 3.0.0'
-
4
require 'bcrypt'
-
-
4
attr_reader :password
-
-
4
if options.fetch(:validations, true)
-
2
validates_confirmation_of :password
-
2
validates_presence_of :password, :on => :create
-
-
4
before_create { raise "Password digest missing on new record" if password_digest.blank? }
-
end
-
-
4
include InstanceMethodsOnActivation
-
-
4
if respond_to?(:attributes_protected_by_default)
-
def self.attributes_protected_by_default #:nodoc:
-
super + ['password_digest']
-
end
-
end
-
end
-
end
-
-
1
module InstanceMethodsOnActivation
-
# Returns +self+ if the password is correct, otherwise +false+.
-
#
-
# class User < ActiveRecord::Base
-
# has_secure_password validations: false
-
# end
-
#
-
# user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
-
# user.save
-
# user.authenticate('notright') # => false
-
# user.authenticate('mUc3m00RsqyRe') # => user
-
1
def authenticate(unencrypted_password)
-
2
BCrypt::Password.new(password_digest) == unencrypted_password && self
-
end
-
-
# Encrypts the password into the +password_digest+ attribute, only if the
-
# new password is not blank.
-
#
-
# class User < ActiveRecord::Base
-
# has_secure_password validations: false
-
# end
-
#
-
# user = User.new
-
# user.password = nil
-
# user.password_digest # => nil
-
# user.password = 'mUc3m00RsqyRe'
-
# user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
-
1
def password=(unencrypted_password)
-
10
unless unencrypted_password.blank?
-
5
@password = unencrypted_password
-
5
self.password_digest = BCrypt::Password.create(unencrypted_password)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
# == Active \Model \Serialization
-
#
-
# Provides a basic serialization to a serializable_hash for your object.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Serialization
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => nil}
-
# end
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
#
-
# You need to declare an attributes hash which contains the attributes you
-
# want to serialize. Attributes must be strings, not symbols. When called,
-
# serializable hash will use instance methods that match the name of the
-
# attributes hash's keys. In order to override this behavior, take a look at
-
# the private method +read_attribute_for_serialization+.
-
#
-
# Most of the time though, you will want to include the JSON or XML
-
# serializations. Both of these modules automatically include the
-
# <tt>ActiveModel::Serialization</tt> module, so there is no need to
-
# explicitly include it.
-
#
-
# A minimal implementation including XML and JSON would be:
-
#
-
# class Person
-
# include ActiveModel::Serializers::JSON
-
# include ActiveModel::Serializers::Xml
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => nil}
-
# end
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.as_json # => {"name"=>nil}
-
# person.to_json # => "{\"name\":null}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
# person.as_json # => {"name"=>"Bob"}
-
# person.to_json # => "{\"name\":\"Bob\"}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
-
# <tt>:include</tt>. The following are all valid examples:
-
#
-
# person.serializable_hash(only: 'name')
-
# person.serializable_hash(include: :address)
-
# person.serializable_hash(include: { address: { only: 'city' }})
-
1
module Serialization
-
# Returns a serialized hash of your object.
-
#
-
# class Person
-
# include ActiveModel::Serialization
-
#
-
# attr_accessor :name, :age
-
#
-
# def attributes
-
# {'name' => nil, 'age' => nil}
-
# end
-
#
-
# def capitalized_name
-
# name.capitalize
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'bob'
-
# person.age = 22
-
# person.serializable_hash # => {"name"=>"bob", "age"=>22}
-
# person.serializable_hash(only: :name) # => {"name"=>"bob"}
-
# person.serializable_hash(except: :name) # => {"age"=>22}
-
# person.serializable_hash(methods: :capitalized_name)
-
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
-
1
def serializable_hash(options = nil)
-
106
options ||= {}
-
-
106
attribute_names = attributes.keys
-
106
if only = options[:only]
-
14
attribute_names &= Array(only).map(&:to_s)
-
elsif except = options[:except]
-
7
attribute_names -= Array(except).map(&:to_s)
-
end
-
-
106
hash = {}
-
476
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
-
-
116
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }
-
-
106
serializable_add_includes(options) do |association, records, opts|
-
14
hash[association.to_s] = if records.respond_to?(:to_ary)
-
25
records.to_ary.map { |a| a.serializable_hash(opts) }
-
else
-
4
records.serializable_hash(opts)
-
end
-
end
-
-
106
hash
-
end
-
-
1
private
-
-
# Hook method defining how an attribute value should be retrieved for
-
# serialization. By default this is assumed to be an instance named after
-
# the attribute. Override this method in subclasses should you need to
-
# retrieve the value for a given attribute differently:
-
#
-
# class MyClass
-
# include ActiveModel::Validations
-
#
-
# def initialize(data = {})
-
# @data = data
-
# end
-
#
-
# def read_attribute_for_serialization(key)
-
# @data[key]
-
# end
-
# end
-
1
alias :read_attribute_for_serialization :send
-
-
# Add associations specified via the <tt>:include</tt> option.
-
#
-
# Expects a block that takes as arguments:
-
# +association+ - name of the association
-
# +records+ - the association record(s) to be serialized
-
# +opts+ - options for the association records
-
1
def serializable_add_includes(options = {}) #:nodoc:
-
158
return unless includes = options[:include]
-
-
24
unless includes.is_a?(Hash)
-
35
includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
-
end
-
-
24
includes.each do |association, opts|
-
27
if records = send(association)
-
27
yield association, records, opts
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/json'
-
-
1
module ActiveModel
-
1
module Serializers
-
# == Active Model JSON Serializer
-
1
module JSON
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
included do
-
1
extend ActiveModel::Naming
-
-
1
class_attribute :include_root_in_json
-
1
self.include_root_in_json = false
-
end
-
-
# Returns a hash representing the model. Some configuration can be
-
# passed through +options+.
-
#
-
# The option <tt>include_root_in_json</tt> controls the top-level behavior
-
# of +as_json+. If +true+, +as_json+ will emit a single root node named
-
# after the object's type. The default value for <tt>include_root_in_json</tt>
-
# option is +false+.
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true}
-
#
-
# ActiveRecord::Base.include_root_in_json = true
-
#
-
# user.as_json
-
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true } }
-
#
-
# This behavior can also be achieved by setting the <tt>:root</tt> option
-
# to +true+ as in:
-
#
-
# user = User.find(1)
-
# user.as_json(root: true)
-
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true } }
-
#
-
# Without any +options+, the returned Hash will include all the model's
-
# attributes.
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true}
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
-
# the attributes included, and work similar to the +attributes+ method.
-
#
-
# user.as_json(only: [:id, :name])
-
# # => { "id" => 1, "name" => "Konata Izumi" }
-
#
-
# user.as_json(except: [:id, :created_at, :age])
-
# # => { "name" => "Konata Izumi", "awesome" => true }
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>:
-
#
-
# user.as_json(methods: :permalink)
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "permalink" => "1-konata-izumi" }
-
#
-
# To include associations use <tt>:include</tt>:
-
#
-
# user.as_json(include: :posts)
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
-
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
-
#
-
# Second level and higher order associations work as well:
-
#
-
# user.as_json(include: { posts: {
-
# include: { comments: {
-
# only: :body } },
-
# only: :title } })
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
-
# # "title" => "Welcome to the weblog" },
-
# # { "comments" => [ { "body" => "Don't think too hard" } ],
-
# # "title" => "So I was thinking" } ] }
-
1
def as_json(options = nil)
-
16
root = if options && options.key?(:root)
-
4
options[:root]
-
else
-
12
include_root_in_json
-
end
-
-
16
if root
-
5
root = self.class.model_name.element if root == true
-
5
{ root => serializable_hash(options) }
-
else
-
11
serializable_hash(options)
-
end
-
end
-
-
# Sets the model +attributes+ from a JSON string. Returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Serializers::JSON
-
#
-
# attr_accessor :name, :age, :awesome
-
#
-
# def attributes=(hash)
-
# hash.each do |key, value|
-
# instance_variable_set("@#{key}", value)
-
# end
-
# end
-
#
-
# def attributes
-
# instance_values
-
# end
-
# end
-
#
-
# json = { name: 'bob', age: 22, awesome:true }.to_json
-
# person = Person.new
-
# person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
#
-
# The default value for +include_root+ is +false+. You can change it to
-
# +true+ if the given JSON string includes a single root node.
-
#
-
# json = { person: { name: 'bob', age: 22, awesome:true } }.to_json
-
# person = Person.new
-
# person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
1
def from_json(json, include_root=include_root_in_json)
-
3
hash = ActiveSupport::JSON.decode(json)
-
3
hash = hash.values.first if include_root
-
3
self.attributes = hash
-
3
self
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
1
module Serializers
-
# == Active Model XML Serializer
-
1
module Xml
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
included do
-
2
extend ActiveModel::Naming
-
end
-
-
1
class Serializer #:nodoc:
-
1
class Attribute #:nodoc:
-
1
attr_reader :name, :value, :type
-
-
1
def initialize(name, serializable, value)
-
216
@name, @serializable = name, serializable
-
216
value = value.in_time_zone if value.respond_to?(:in_time_zone)
-
216
@value = value
-
216
@type = compute_type
-
end
-
-
1
def decorations
-
216
decorations = {}
-
216
decorations[:encoding] = 'base64' if type == :binary
-
216
decorations[:type] = (type == :string) ? nil : type
-
216
decorations[:nil] = true if value.nil?
-
216
decorations
-
end
-
-
1
protected
-
-
1
def compute_type
-
216
return if value.nil?
-
215
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
-
215
type ||= :string if value.respond_to?(:to_str)
-
215
type ||= :yaml
-
215
type
-
end
-
end
-
-
1
class MethodAttribute < Attribute #:nodoc:
-
end
-
-
1
attr_reader :options
-
-
1
def initialize(serializable, options = nil)
-
52
@serializable = serializable
-
52
@options = options ? options.dup : {}
-
end
-
-
1
def serializable_hash
-
52
@serializable.serializable_hash(@options.except(:include))
-
end
-
-
1
def serializable_collection
-
52
methods = Array(options[:methods]).map(&:to_s)
-
52
serializable_hash.map do |name, value|
-
216
name = name.to_s
-
216
if methods.include?(name)
-
3
self.class::MethodAttribute.new(name, @serializable, value)
-
else
-
213
self.class::Attribute.new(name, @serializable, value)
-
end
-
end
-
end
-
-
1
def serialize
-
52
require 'builder' unless defined? ::Builder
-
-
52
options[:indent] ||= 2
-
52
options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
-
-
52
@builder = options[:builder]
-
52
@builder.instruct! unless options[:skip_instruct]
-
-
52
root = (options[:root] || @serializable.class.model_name.element).to_s
-
52
root = ActiveSupport::XmlMini.rename_key(root, options)
-
-
52
args = [root]
-
52
args << {:xmlns => options[:namespace]} if options[:namespace]
-
52
args << {:type => options[:type]} if options[:type] && !options[:skip_types]
-
-
52
@builder.tag!(*args) do
-
52
add_attributes_and_methods
-
52
add_includes
-
52
add_extra_behavior
-
52
add_procs
-
52
yield @builder if block_given?
-
end
-
end
-
-
1
private
-
-
1
def add_extra_behavior
-
end
-
-
1
def add_attributes_and_methods
-
52
serializable_collection.each do |attribute|
-
216
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
-
216
ActiveSupport::XmlMini.to_tag(key, attribute.value,
-
options.merge(attribute.decorations))
-
end
-
end
-
-
1
def add_includes
-
52
@serializable.send(:serializable_add_includes, options) do |association, records, opts|
-
13
add_associations(association, records, opts)
-
end
-
end
-
-
# TODO: This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
-
1
def add_associations(association, records, opts)
-
13
merged_options = opts.merge(options.slice(:builder, :indent))
-
13
merged_options[:skip_instruct] = true
-
-
13
[:skip_types, :dasherize, :camelize].each do |key|
-
39
merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil?
-
end
-
-
13
if records.respond_to?(:to_ary)
-
4
records = records.to_ary
-
-
4
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
-
4
type = options[:skip_types] ? { } : {:type => "array"}
-
4
association_name = association.to_s.singularize
-
4
merged_options[:root] = association_name
-
-
4
if records.empty?
-
@builder.tag!(tag, type)
-
else
-
4
@builder.tag!(tag, type) do
-
4
records.each do |record|
-
8
if options[:skip_types]
-
2
record_type = {}
-
else
-
6
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
-
6
record_type = {:type => record_class}
-
end
-
-
8
record.to_xml merged_options.merge(record_type)
-
end
-
end
-
end
-
else
-
9
merged_options[:root] = association.to_s
-
9
records.to_xml(merged_options)
-
end
-
end
-
-
1
def add_procs
-
52
if procs = options.delete(:procs)
-
2
Array(procs).each do |proc|
-
2
if proc.arity == 1
-
1
proc.call(options)
-
else
-
1
proc.call(options, @serializable)
-
end
-
end
-
end
-
end
-
end
-
-
# Returns XML representing the model. Configuration can be
-
# passed through +options+.
-
#
-
# Without any +options+, the returned XML string will include all the
-
# model's attributes.
-
#
-
# user = User.find(1)
-
# user.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <user>
-
# <id type="integer">1</id>
-
# <name>David</name>
-
# <age type="integer">16</age>
-
# <created-at type="dateTime">2011-01-30T22:29:23Z</created-at>
-
# </user>
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
-
# attributes included, and work similar to the +attributes+ method.
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>.
-
#
-
# To include associations use <tt>:include</tt>.
-
#
-
# For further documentation, see <tt>ActiveRecord::Serialization#to_xml</tt>
-
1
def to_xml(options = {}, &block)
-
52
Serializer.new(self, options).serialize(&block)
-
end
-
-
# Sets the model +attributes+ from a JSON string. Returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Serializers::Xml
-
#
-
# attr_accessor :name, :age, :awesome
-
#
-
# def attributes=(hash)
-
# hash.each do |key, value|
-
# instance_variable_set("@#{key}", value)
-
# end
-
# end
-
#
-
# def attributes
-
# instance_values
-
# end
-
# end
-
#
-
# xml = { name: 'bob', age: 22, awesome:true }.to_xml
-
# person = Person.new
-
# person.from_xml(xml) # => #<Person:0x007fec5e3b3c40 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
1
def from_xml(xml)
-
self.attributes = Hash.from_xml(xml).values.first
-
self
-
end
-
end
-
end
-
end
-
1
module ActiveModel #:nodoc:
-
1
class TestCase < ActiveSupport::TestCase #:nodoc:
-
end
-
end
-
1
module ActiveModel
-
-
# == Active \Model \Translation
-
#
-
# Provides integration between your object and the Rails internationalization
-
# (i18n) framework.
-
#
-
# A minimal implementation could be:
-
#
-
# class TranslatedPerson
-
# extend ActiveModel::Translation
-
# end
-
#
-
# TranslatedPerson.human_attribute_name('my_attribute')
-
# # => "My attribute"
-
#
-
# This also provides the required class methods for hooking into the
-
# Rails internationalization API, including being able to define a
-
# class based +i18n_scope+ and +lookup_ancestors+ to find translations in
-
# parent classes.
-
1
module Translation
-
1
include ActiveModel::Naming
-
-
# Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
-
1
def i18n_scope
-
1690
:activemodel
-
end
-
-
# When localizing a string, it goes through the lookup returned by this
-
# method, which is used in ActiveModel::Name#human,
-
# ActiveModel::Errors#full_messages and
-
# ActiveModel::Translation#human_attribute_name.
-
1
def lookup_ancestors
-
13156
self.ancestors.select { |x| x.respond_to?(:model_name) }
-
end
-
-
# Transforms attribute names into a more human format, such as "First name"
-
# instead of "first_name".
-
#
-
# Person.human_attribute_name("first_name") # => "First name"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human_attribute_name(attribute, options = {})
-
377
options = { :count => 1 }.merge!(options)
-
377
parts = attribute.to_s.split(".")
-
377
attribute = parts.pop
-
377
namespace = parts.join("/") unless parts.empty?
-
377
attributes_scope = "#{self.i18n_scope}.attributes"
-
-
377
if namespace
-
4
defaults = lookup_ancestors.map do |klass|
-
4
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
-
end
-
4
defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
-
else
-
373
defaults = lookup_ancestors.map do |klass|
-
388
:"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
-
end
-
end
-
-
377
defaults << :"attributes.#{attribute}"
-
377
defaults << options.delete(:default) if options[:default]
-
377
defaults << attribute.humanize
-
-
377
options[:default] = defaults
-
377
I18n.translate(defaults.shift, options)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_model/errors'
-
1
require 'active_model/validations/callbacks'
-
1
require 'active_model/validator'
-
-
1
module ActiveModel
-
-
# == Active \Model Validations
-
#
-
# Provides a full validation framework to your objects.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Which provides you with the full standard validation stack that you
-
# know from Active Record:
-
#
-
# person = Person.new
-
# person.valid? # => true
-
# person.invalid? # => false
-
#
-
# person.first_name = 'zoolander'
-
# person.valid? # => false
-
# person.invalid? # => true
-
# person.errors.messages # => {first_name:["starts with z."]}
-
#
-
# Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+
-
# method to your instances initialized with a new <tt>ActiveModel::Errors</tt>
-
# object, so there is no need for you to do this manually.
-
1
module Validations
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
12
extend ActiveModel::Callbacks
-
12
extend ActiveModel::Translation
-
-
12
extend HelperMethods
-
12
include HelperMethods
-
-
12
attr_accessor :validation_context
-
12
define_callbacks :validate, :scope => :name
-
-
12
class_attribute :_validators
-
79
self._validators = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
module ClassMethods
-
# Validates each attribute against a block.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name, allow_blank: true do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Options:
-
# * <tt>:on</tt> - Specifies the context where this validation is active
-
# (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
-
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
1
def validates_each(*attr_names, &block)
-
2
validates_with BlockValidator, _merge_attributes(attr_names), &block
-
end
-
-
# Adds a validation method or block to the class. This is useful when
-
# overriding the +validate+ instance method becomes too unwieldy and
-
# you're looking for more descriptive declaration of your validations.
-
#
-
# This can be done with a symbol pointing to a method:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate :must_be_friends
-
#
-
# def must_be_friends
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# With a block which is passed with the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do |comment|
-
# comment.must_be_friends
-
# end
-
#
-
# def must_be_friends
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# Or with a block where self points to the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# Options:
-
# * <tt>:on</tt> - Specifies the context where this validation is active
-
# (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>)
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
-
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
1
def validate(*args, &block)
-
311
options = args.extract_options!
-
311
if options.key?(:on)
-
9
options = options.dup
-
9
options[:if] = Array(options[:if])
-
9
options[:if].unshift("validation_context == :#{options[:on]}")
-
end
-
311
args << options
-
311
set_callback(:validate, *args, &block)
-
end
-
-
# List all validators that are being used to validate the model using
-
# +validates_with+ method.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validates_with MyValidator
-
# validates_with OtherValidator, on: :create
-
# validates_with StrictValidator, strict: true
-
# end
-
#
-
# Person.validators
-
# # => [
-
# # #<MyValidator:0x007fbff403e808 @options={}>,
-
# # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
-
# # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
-
# # ]
-
1
def validators
-
2
_validators.values.flatten.uniq
-
end
-
-
# List all validators that are being used to validate a specific attribute.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name , :age
-
#
-
# validates_presence_of :name
-
# validates_inclusion_of :age, in: 0..99
-
# end
-
#
-
# Person.validators_on(:name)
-
# # => [
-
# # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
-
# # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={in:0..99}>
-
# # ]
-
1
def validators_on(*attributes)
-
7
attributes.flat_map do |attribute|
-
8
_validators[attribute.to_sym]
-
end
-
end
-
-
# Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# end
-
#
-
# User.attribute_method?(:name) # => true
-
# User.attribute_method?(:age) # => false
-
1
def attribute_method?(attribute)
-
26
method_defined?(attribute)
-
end
-
-
# Copy validators on inheritance.
-
1
def inherited(base) #:nodoc:
-
9
dup = _validators.dup
-
9
base._validators = dup.each { |k, v| dup[k] = v.dup }
-
9
super
-
end
-
end
-
-
# Clean the +Errors+ object if instance is duped.
-
1
def initialize_dup(other) #:nodoc:
-
1
@errors = nil
-
1
super
-
end
-
-
# Returns the +Errors+ object that holds all information about attribute
-
# error messages.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.valid? # => false
-
# person.errors # => #<ActiveModel::Errors:0x007fe603816640 @messages={name:["can't be blank"]}>
-
1
def errors
-
2087
@errors ||= Errors.new(self)
-
end
-
-
# Runs all the specified validations and returns +true+ if no errors were
-
# added otherwise +false+.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid? # => false
-
# person.name = 'david'
-
# person.valid? # => true
-
#
-
# Context can optionally be supplied to define which callbacks to test
-
# against (the context is defined on the validations using <tt>:on</tt>).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name, on: :new
-
# end
-
#
-
# person = Person.new
-
# person.valid? # => true
-
# person.valid?(:new) # => false
-
1
def valid?(context = nil)
-
598
current_context, self.validation_context = validation_context, context
-
598
errors.clear
-
598
run_validations!
-
ensure
-
598
self.validation_context = current_context
-
end
-
-
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
-
# added, +false+ otherwise.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.invalid? # => true
-
# person.name = 'david'
-
# person.invalid? # => false
-
#
-
# Context can optionally be supplied to define which callbacks to test
-
# against (the context is defined on the validations using <tt>:on</tt>).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name, on: :new
-
# end
-
#
-
# person = Person.new
-
# person.invalid? # => false
-
# person.invalid?(:new) # => true
-
1
def invalid?(context = nil)
-
233
!valid?(context)
-
end
-
-
# Hook method defining how an attribute value should be retrieved. By default
-
# this is assumed to be an instance named after the attribute. Override this
-
# method in subclasses should you need to retrieve the value for a given
-
# attribute differently:
-
#
-
# class MyClass
-
# include ActiveModel::Validations
-
#
-
# def initialize(data = {})
-
# @data = data
-
# end
-
#
-
# def read_attribute_for_validation(key)
-
# @data[key]
-
# end
-
# end
-
1
alias :read_attribute_for_validation :send
-
-
1
protected
-
-
1
def run_validations! #:nodoc:
-
597
run_callbacks :validate
-
590
errors.empty?
-
end
-
end
-
end
-
-
1
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
-
12
filename = File.basename(path)
-
12
require "active_model/validations/#{filename}"
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class AcceptanceValidator < EachValidator # :nodoc:
-
1
def initialize(options)
-
13
super({ :allow_nil => true, :accept => "1" }.merge!(options))
-
end
-
-
1
def validate_each(record, attribute, value)
-
16
unless value == options[:accept]
-
12
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
-
end
-
end
-
-
1
def setup(klass)
-
26
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
-
26
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
-
13
klass.send(:attr_reader, *attr_readers)
-
13
klass.send(:attr_writer, *attr_writers)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate the acceptance of a
-
# terms of service check box (or similar agreement).
-
#
-
# class Person < ActiveRecord::Base
-
# validates_acceptance_of :terms_of_service
-
# validates_acceptance_of :eula, message: 'must be abided'
-
# end
-
#
-
# If the database column does not exist, the +terms_of_service+ attribute
-
# is entirely virtual. This check is performed only if +terms_of_service+
-
# is not +nil+ and by default on save.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "must be
-
# accepted").
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default
-
# is +true+).
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
-
# The default value is a string "1", which makes it easy to relate to
-
# an HTML checkbox. This should be set to +true+ if you are validating
-
# a database column, since the attribute is typecast from "1" to +true+
-
# before validation.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_acceptance_of(*attr_names)
-
13
validates_with AcceptanceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/callbacks'
-
-
1
module ActiveModel
-
1
module Validations
-
# == Active \Model Validation Callbacks
-
#
-
# Provides an interface for any class to have +before_validation+ and
-
# +after_validation+ callbacks.
-
#
-
# First, include ActiveModel::Validations::Callbacks from the class you are
-
# creating:
-
#
-
# class MyModel
-
# include ActiveModel::Validations::Callbacks
-
#
-
# before_validation :do_stuff_before_validation
-
# after_validation :do_stuff_after_validation
-
# end
-
#
-
# Like other <tt>before_*</tt> callbacks if +before_validation+ returns
-
# +false+ then <tt>valid?</tt> will not be called.
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
include ActiveSupport::Callbacks
-
2
define_callbacks :validation, :terminator => "result == false", :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name]
-
end
-
-
1
module ClassMethods
-
# Defines a callback that will get called right before validation
-
# happens.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# include ActiveModel::Validations::Callbacks
-
#
-
# attr_accessor :name
-
#
-
# validates_length_of :name, maximum: 6
-
#
-
# before_validation :remove_whitespaces
-
#
-
# private
-
#
-
# def remove_whitespaces
-
# name.strip!
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = ' bob '
-
# person.valid? # => true
-
# person.name # => "bob"
-
1
def before_validation(*args, &block)
-
7
options = args.last
-
7
if options.is_a?(Hash) && options[:on]
-
options[:if] = Array(options[:if])
-
options[:on] = Array(options[:on])
-
options[:if].unshift("#{options[:on]}.include? self.validation_context")
-
end
-
7
set_callback(:validation, :before, *args, &block)
-
end
-
-
# Defines a callback that will get called right after validation
-
# happens.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# include ActiveModel::Validations::Callbacks
-
#
-
# attr_accessor :name, :status
-
#
-
# validates_presence_of :name
-
#
-
# after_validation :set_status
-
#
-
# private
-
#
-
# def set_status
-
# self.status = errors.empty?
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid? # => false
-
# person.status # => false
-
# person.name = 'bob'
-
# person.valid? # => true
-
# person.status # => true
-
1
def after_validation(*args, &block)
-
3
options = args.extract_options!
-
3
options[:prepend] = true
-
3
options[:if] = Array(options[:if])
-
3
if options[:on]
-
options[:on] = Array(options[:on])
-
options[:if].unshift("#{options[:on]}.include? self.validation_context")
-
end
-
3
set_callback(:validation, :after, *(args << options), &block)
-
end
-
end
-
-
1
protected
-
-
# Overwrite run validations to include callbacks.
-
1
def run_validations! #:nodoc:
-
847
run_callbacks(:validation) { super }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/range'
-
-
1
module ActiveModel
-
1
module Validations
-
1
module Clusivity #:nodoc:
-
1
ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " <<
-
"and must be supplied as the :in (or :within) option of the configuration hash"
-
-
1
def check_validity!
-
46
unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
-
2
raise ArgumentError, ERROR_MESSAGE
-
end
-
end
-
-
1
private
-
-
1
def include?(record, value)
-
60
exclusions = if delimiter.respond_to?(:call)
-
4
delimiter.call(record)
-
elsif delimiter.respond_to?(:to_sym)
-
4
record.send(delimiter)
-
else
-
52
delimiter
-
end
-
-
60
exclusions.send(inclusion_method(exclusions), value)
-
end
-
-
1
def delimiter
-
232
@delimiter ||= options[:in] || options[:within]
-
end
-
-
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
-
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
-
# uses the previous logic of comparing a value with the range endpoints.
-
1
def inclusion_method(enumerable)
-
60
enumerable.is_a?(Range) ? :cover? : :include?
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class ConfirmationValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attribute, value)
-
23
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
-
14
human_attribute_name = record.class.human_attribute_name(attribute)
-
14
record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(:attribute => human_attribute_name))
-
end
-
end
-
-
1
def setup(klass)
-
15
klass.send(:attr_accessor, *attributes.map do |attribute|
-
15
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
-
end.compact)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate a password or email
-
# address field with a confirmation.
-
#
-
# Model:
-
# class Person < ActiveRecord::Base
-
# validates_confirmation_of :user_name, :password
-
# validates_confirmation_of :email_address,
-
# message: 'should match confirmation'
-
# end
-
#
-
# View:
-
# <%= password_field "person", "password" %>
-
# <%= password_field "person", "password_confirmation" %>
-
#
-
# The added +password_confirmation+ attribute is virtual; it exists only
-
# as an in-memory attribute for validating the password. To achieve this,
-
# the validation adds accessors to the model for the confirmation
-
# attribute.
-
#
-
# NOTE: This check is performed only if +password_confirmation+ is not
-
# +nil+. To require confirmation, make sure to add a presence check for
-
# the confirmation attribute:
-
#
-
# validates_presence_of :password_confirmation, if: :password_changed?
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
-
# confirmation").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_confirmation_of(*attr_names)
-
14
validates_with ConfirmationValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require "active_model/validations/clusivity"
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class ExclusionValidator < EachValidator # :nodoc:
-
1
include Clusivity
-
-
1
def validate_each(record, attribute, value)
-
23
if include?(record, value)
-
19
record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(:value => value))
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the value of the specified attribute is not in a
-
# particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_exclusion_of :username, in: %w( admin superuser ), message: "You don't belong here"
-
# validates_exclusion_of :age, in: 30..60, message: 'This site is only for under 30 and over 60'
-
# validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
-
# validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
-
# message: 'should not be the same as your username or first name'
-
# validates_exclusion_of :karma, in: :reserved_karmas
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
-
# be part of. This can be supplied as a proc, lambda or symbol which returns an
-
# enumerable. If the enumerable is a range the test is performed with
-
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
-
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
-
# reserved").
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the
-
# attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the
-
# attribute is blank(default is +false+).
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_exclusion_of(*attr_names)
-
19
validates_with ExclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class FormatValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attribute, value)
-
37
if options[:with]
-
33
regexp = option_call(record, :with)
-
33
record_error(record, attribute, :with, value) if value.to_s !~ regexp
-
4
elsif options[:without]
-
4
regexp = option_call(record, :without)
-
4
record_error(record, attribute, :without, value) if value.to_s =~ regexp
-
end
-
end
-
-
1
def check_validity!
-
25
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
-
3
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
-
end
-
-
22
check_options_validity(options, :with)
-
20
check_options_validity(options, :without)
-
end
-
-
1
private
-
-
1
def option_call(record, name)
-
37
option = options[name]
-
37
option.respond_to?(:call) ? option.call(record) : option
-
end
-
-
1
def record_error(record, attribute, name, value)
-
21
record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
-
end
-
-
1
def regexp_using_multiline_anchors?(regexp)
-
regexp.source.start_with?("^") ||
-
18
(regexp.source.end_with?("$") && !regexp.source.end_with?("\\$"))
-
end
-
-
1
def check_options_validity(options, name)
-
42
option = options[name]
-
42
if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
-
2
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
-
40
elsif option && option.is_a?(Regexp) &&
-
regexp_using_multiline_anchors?(option) && options[:multiline] != true
-
1
raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
-
"which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
-
":multiline => true option?"
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is of the correct
-
# form, going by the regular expression provided.You can require that the
-
# attribute matches the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create
-
# end
-
#
-
# Alternatively, you can require that the specified attribute does _not_
-
# match the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, without: /NOSPAM/
-
# end
-
#
-
# You can also provide a proc or lambda which will determine the regular
-
# expression that will be used to validate the attribute.
-
#
-
# class Person < ActiveRecord::Base
-
# # Admin can have number as a first letter in their screen name
-
# validates_format_of :screen_name,
-
# with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
-
# end
-
#
-
# Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the
-
# string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
-
#
-
# Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
-
# the <tt>multiline: true</tt> option in case you use any of these two
-
# anchors in the provided regular expression. In most cases, you should be
-
# using <tt>\A</tt> and <tt>\z</tt>.
-
#
-
# You must pass either <tt>:with</tt> or <tt>:without</tt> as an option.
-
# In addition, both must be a regular expression or a proc or lambda, or
-
# else an exception will be raised.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
-
# * <tt>:allow_nil</tt> - If set to true, skips this validation if the
-
# attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to true, skips this validation if the
-
# attribute is blank (default is +false+).
-
# * <tt>:with</tt> - Regular expression that if the attribute matches will
-
# result in a successful validation. This can be provided as a proc or
-
# lambda returning regular expression which will be called at runtime.
-
# * <tt>:without</tt> - Regular expression that if the attribute does not
-
# match will result in a successful validation. This can be provided as
-
# a proc or lambda returning regular expression which will be called at
-
# runtime.
-
# * <tt>:multiline</tt> - Set to true if your regular expression contains
-
# anchors that match the beginning or end of lines as opposed to the
-
# beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_format_of(*attr_names)
-
23
validates_with FormatValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require "active_model/validations/clusivity"
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class InclusionValidator < EachValidator # :nodoc:
-
1
include Clusivity
-
-
1
def validate_each(record, attribute, value)
-
37
unless include?(record, value)
-
27
record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(:value => value))
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is available in a
-
# particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_inclusion_of :gender, in: %w( m f )
-
# validates_inclusion_of :age, in: 0..99
-
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
-
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
-
# validates_inclusion_of :karma, in: :available_karmas
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of available items. This can be
-
# supplied as a proc, lambda or symbol which returns an enumerable. If the
-
# enumerable is a range the test is performed with <tt>Range#cover?</tt>,
-
# otherwise with <tt>include?</tt>.
-
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
-
# not included in the list").
-
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
-
# attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
-
# attribute is blank (default is +false+).
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_inclusion_of(*attr_names)
-
26
validates_with InclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active \Model Length \Validator
-
1
module Validations
-
1
class LengthValidator < EachValidator # :nodoc:
-
1
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
-
1
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
-
-
1
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
-
-
1
def initialize(options)
-
89
if range = (options.delete(:in) || options.delete(:within))
-
31
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
-
29
options[:minimum], options[:maximum] = range.min, range.max
-
end
-
-
87
super
-
end
-
-
1
def check_validity!
-
87
keys = CHECKS.keys & options.keys
-
-
87
if keys.empty?
-
raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
-
end
-
-
87
keys.each do |key|
-
117
value = options[key]
-
-
117
unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY
-
4
raise ArgumentError, ":#{key} must be a nonnegative Integer or Infinity"
-
end
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
105
value = tokenize(value)
-
105
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
-
105
errors_options = options.except(*RESERVED_OPTIONS)
-
-
105
CHECKS.each do |key, validity_check|
-
315
next unless check_value = options[key]
-
152
next if value_length.send(validity_check, check_value)
-
-
71
errors_options[:count] = check_value
-
-
71
default_message = options[MESSAGES[key]]
-
71
errors_options[:message] ||= default_message if default_message
-
-
71
record.errors.add(attribute, MESSAGES[key], errors_options)
-
end
-
end
-
-
1
private
-
-
1
def tokenize(value)
-
if options[:tokenizer] && value.kind_of?(String)
-
2
options[:tokenizer].call(value)
-
105
end || value
-
end
-
end
-
-
1
module HelperMethods
-
-
# Validates that the specified attribute matches the length restrictions
-
# supplied. Only one option can be used at a time:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_length_of :first_name, maximum: 30
-
# validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
-
# validates_length_of :fax, in: 7..32, allow_nil: true
-
# validates_length_of :phone, in: 7..32, allow_blank: true
-
# validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
-
# validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
-
# validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
-
# validates_length_of :essay, minimum: 100, too_short: 'Your essay must be at least 100 words.',
-
# tokenizer: ->(str) { str.scan(/\w+/) }
-
# end
-
#
-
# Configuration options:
-
# * <tt>:minimum</tt> - The minimum size of the attribute.
-
# * <tt>:maximum</tt> - The maximum size of the attribute.
-
# * <tt>:is</tt> - The exact size of the attribute.
-
# * <tt>:within</tt> - A range specifying the minimum and maximum size of
-
# the attribute.
-
# * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
-
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
-
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
-
# * <tt>:too_long</tt> - The error message if the attribute goes over the
-
# maximum (default is: "is too long (maximum is %{count} characters)").
-
# * <tt>:too_short</tt> - The error message if the attribute goes under the
-
# minimum (default is: "is too short (min is %{count} characters)").
-
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
-
# method and the attribute is the wrong size (default is: "is the wrong
-
# length (should be %{count} characters)").
-
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
-
# <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
-
# <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
-
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
-
# (e.g. <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words
-
# as in above example). Defaults to <tt>->(value) { value.split(//) }</tt>
-
# which counts individual characters.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_length_of(*attr_names)
-
85
validates_with LengthValidator, _merge_attributes(attr_names)
-
end
-
-
1
alias_method :validates_size_of, :validates_length_of
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class NumericalityValidator < EachValidator # :nodoc:
-
1
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
-
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
-
:odd => :odd?, :even => :even?, :other_than => :!= }.freeze
-
-
1
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
-
-
1
def check_validity!
-
58
keys = CHECKS.keys - [:odd, :even]
-
58
options.slice(*keys).each do |option, value|
-
25
next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
-
5
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
-
end
-
end
-
-
1
def validate_each(record, attr_name, value)
-
262
before_type_cast = "#{attr_name}_before_type_cast"
-
-
262
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
-
262
raw_value ||= value
-
-
262
return if options[:allow_nil] && raw_value.nil?
-
-
262
unless value = parse_raw_value_as_a_number(raw_value)
-
74
record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
-
74
return
-
end
-
-
188
if options[:only_integer]
-
84
unless value = parse_raw_value_as_an_integer(raw_value)
-
46
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
-
46
return
-
end
-
end
-
-
142
options.slice(*CHECKS.keys).each do |option, option_value|
-
66
case option
-
when :odd, :even
-
20
unless value.to_i.send(CHECKS[option])
-
14
record.errors.add(attr_name, option, filtered_options(value))
-
end
-
else
-
46
option_value = option_value.call(record) if option_value.is_a?(Proc)
-
46
option_value = record.send(option_value) if option_value.is_a?(Symbol)
-
-
46
unless value.send(CHECKS[option], option_value)
-
26
record.errors.add(attr_name, option, filtered_options(value).merge(:count => option_value))
-
end
-
end
-
end
-
end
-
-
1
protected
-
-
1
def parse_raw_value_as_a_number(raw_value)
-
262
case raw_value
-
when /\A0[xX]/
-
nil
-
else
-
250
begin
-
250
Kernel.Float(raw_value)
-
62
rescue ArgumentError, TypeError
-
62
nil
-
end
-
end
-
end
-
-
1
def parse_raw_value_as_an_integer(raw_value)
-
84
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
-
end
-
-
1
def filtered_options(value)
-
160
options.except(*RESERVED_OPTIONS).merge!(:value => value)
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is numeric by
-
# trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
-
# is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt>
-
# (if <tt>only_integer</tt> is set to +true+).
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :value, on: :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
-
# * <tt>:only_integer</tt> - Specifies whether the value has to be an
-
# integer, e.g. an integral value (default is +false+).
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
-
# +false+). Notice that for fixnum and float columns empty strings are
-
# converted to +nil+.
-
# * <tt>:greater_than</tt> - Specifies the value must be greater than the
-
# supplied value.
-
# * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
-
# greater than or equal the supplied value.
-
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
-
# value.
-
# * <tt>:less_than</tt> - Specifies the value must be less than the
-
# supplied value.
-
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
-
# than or equal the supplied value.
-
# * <tt>:other_than</tt> - Specifies the value must be other than the
-
# supplied value.
-
# * <tt>:odd</tt> - Specifies the value must be an odd number.
-
# * <tt>:even</tt> - Specifies the value must be an even number.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+ .
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
#
-
# The following checks can also be supplied with a proc or a symbol which
-
# corresponds to a method:
-
#
-
# * <tt>:greater_than</tt>
-
# * <tt>:greater_than_or_equal_to</tt>
-
# * <tt>:equal_to</tt>
-
# * <tt>:less_than</tt>
-
# * <tt>:less_than_or_equal_to</tt>
-
#
-
# For example:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :width, less_than: ->(person) { person.height }
-
# validates_numericality_of :width, greater_than: :minimum_weight
-
# end
-
1
def validates_numericality_of(*attr_names)
-
55
validates_with NumericalityValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class PresenceValidator < EachValidator # :nodoc:
-
1
def validate(record)
-
55
record.errors.add_on_blank(attributes, options)
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the specified attributes are not blank (as defined by
-
# Object#blank?). Happens by default on save.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_presence_of :first_name
-
# end
-
#
-
# The first_name attribute must be in the object and it cannot be blank.
-
#
-
# If you want to validate the presence of a boolean field (where the real
-
# values are +true+ and +false+), you will want to use
-
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
-
#
-
# This is due to the way Object#blank? handles boolean values:
-
# <tt>false.blank? # => true</tt>.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_presence_of(*attr_names)
-
35
validates_with PresenceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
1
module Validations
-
1
module ClassMethods
-
# This method is a shortcut to all default validators and any custom
-
# validator classes ending in 'Validator'. Note that Rails default
-
# validators can be overridden inside specific classes by creating
-
# custom validator classes in their place such as PresenceValidator.
-
#
-
# Examples of using the default rails validators:
-
#
-
# validates :terms, acceptance: true
-
# validates :password, confirmation: true
-
# validates :username, exclusion: { in: %w(admin superuser) }
-
# validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, on: :create }
-
# validates :age, inclusion: { in: 0..9 }
-
# validates :first_name, length: { maximum: 30 }
-
# validates :age, numericality: true
-
# validates :username, presence: true
-
# validates :username, uniqueness: true
-
#
-
# The power of the +validates+ method comes when using custom validators
-
# and default validators in one call for a given attribute.
-
#
-
# class EmailValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, (options[:message] || "is not an email") unless
-
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
-
# end
-
# end
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :name, :email
-
#
-
# validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
-
# validates :email, presence: true, email: true
-
# end
-
#
-
# Validator classes may also exist within the class being validated
-
# allowing custom modules of validators to be included as needed.
-
#
-
# class Film
-
# include ActiveModel::Validations
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
-
# end
-
# end
-
#
-
# validates :name, title: true
-
# end
-
#
-
# Additionally validator classes may be in another namespace and still
-
# used within any class.
-
#
-
# validates :name, :'film/title' => true
-
#
-
# The validators hash can also handle regular expressions, ranges, arrays
-
# and strings in shortcut form.
-
#
-
# validates :email, format: /@/
-
# validates :gender, inclusion: %w(male female)
-
# validates :password, length: 6..20
-
#
-
# When using shortcut form, ranges and arrays are passed to your
-
# validator's initializer as <tt>options[:in]</tt> while other types
-
# including regular expressions and strings are passed as <tt>options[:with]</tt>.
-
#
-
# There is also a list of options that could be used along with validators:
-
#
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:strict</tt> - if the <tt>:strict</tt> option is set to true
-
# will raise ActiveModel::StrictValidationFailed instead of adding the error.
-
# <tt>:strict</tt> option can also be set to any other exception.
-
#
-
# Example:
-
#
-
# validates :password, presence: true, confirmation: true, if: :password_required?
-
# validates :token, uniqueness: true, strict: TokenGenerationException
-
#
-
#
-
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
-
# and +:message+ can be given to one specific validator, as a hash:
-
#
-
# validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
-
1
def validates(*attributes)
-
31
defaults = attributes.extract_options!.dup
-
31
validations = defaults.slice!(*_validates_default_keys)
-
-
31
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
-
31
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
-
-
31
defaults[:attributes] = attributes
-
-
31
validations.each do |key, options|
-
36
next unless options
-
35
key = "#{key.to_s.camelize}Validator"
-
-
35
begin
-
35
validator = key.include?('::') ? key.constantize : const_get(key)
-
rescue NameError
-
1
raise ArgumentError, "Unknown validator: '#{key}'"
-
end
-
-
34
validates_with(validator, defaults.merge(_parse_validates_options(options)))
-
end
-
end
-
-
# This method is used to define validations that cannot be corrected by end
-
# users and are considered exceptional. So each validator defined with bang
-
# or <tt>:strict</tt> option set to <tt>true</tt> will always raise
-
# <tt>ActiveModel::StrictValidationFailed</tt> instead of adding error
-
# when validation fails. See <tt>validates</tt> for more information about
-
# the validation itself.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates! :name, presence: true
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid?
-
# # => ActiveModel::StrictValidationFailed: Name can't be blank
-
1
def validates!(*attributes)
-
1
options = attributes.extract_options!
-
1
options[:strict] = true
-
1
validates(*(attributes << options))
-
end
-
-
1
protected
-
-
# When creating custom validators, it might be useful to be able to specify
-
# additional default keys. This can be done by overwriting this method.
-
1
def _validates_default_keys # :nodoc:
-
31
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
-
end
-
-
1
def _parse_validates_options(options) # :nodoc:
-
34
case options
-
when TrueClass
-
18
{}
-
when Hash
-
9
options
-
when Range, Array
-
2
{ :in => options }
-
else
-
5
{ :with => options }
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module Validations
-
1
module HelperMethods
-
1
private
-
1
def _merge_attributes(attr_names)
-
272
options = attr_names.extract_options!.symbolize_keys
-
272
attr_names.flatten!
-
272
options[:attributes] = attr_names
-
272
options
-
end
-
end
-
-
1
class WithValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attr, val)
-
4
method_name = options[:with]
-
-
4
if record.method(method_name).arity == 0
-
2
record.send method_name
-
else
-
2
record.send method_name, attr
-
end
-
end
-
end
-
-
1
module ClassMethods
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors.add :base, 'This record is invalid'
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, MyOtherValidator, on: :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:on</tt> - Specifies when this validation is active
-
# (<tt>:create</tt> or <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
-
# The method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur
-
# (e.g. <tt>unless: :skip_validation</tt>, or
-
# <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
-
# The method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:strict</tt> - Specifies whether validation should be strict.
-
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as +options+:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, my_custom_key: 'my custom value'
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# options[:my_custom_key] # => "my custom value"
-
# end
-
# end
-
1
def validates_with(*args, &block)
-
321
options = args.extract_options!
-
321
args.each do |klass|
-
322
validator = klass.new(options, &block)
-
301
validator.setup(self) if validator.respond_to?(:setup)
-
-
301
if validator.respond_to?(:attributes) && !validator.attributes.empty?
-
286
validator.attributes.each do |attribute|
-
307
_validators[attribute.to_sym] << validator
-
end
-
else
-
15
_validators[nil] << validator
-
end
-
-
301
validate(validator, options)
-
end
-
end
-
end
-
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations
-
#
-
# def instance_validations
-
# validates_with MyValidator
-
# end
-
# end
-
#
-
# Please consult the class method documentation for more information on
-
# creating your own validator.
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations, on: :create
-
#
-
# def instance_validations
-
# validates_with MyValidator, MyOtherValidator
-
# end
-
# end
-
#
-
# Standard configuration options (<tt>:on</tt>, <tt>:if</tt> and
-
# <tt>:unless</tt>), which are available on the class version of
-
# +validates_with+, should instead be placed on the +validates+ method
-
# as these are applied and tested in the callback.
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as +options+, please refer to the
-
# class version of this method for more information.
-
1
def validates_with(*args, &block)
-
4
options = args.extract_options!
-
4
args.each do |klass|
-
4
validator = klass.new(options, &block)
-
4
validator.validate(self)
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/module/anonymous"
-
-
1
module ActiveModel
-
-
# == Active \Model \Validator
-
#
-
# A simple base class that can be used along with
-
# ActiveModel::Validations::ClassMethods.validates_with
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors[:base] = "This record is invalid"
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# Any class that inherits from ActiveModel::Validator must implement a method
-
# called +validate+ which accepts a +record+.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record # => The person instance being validated
-
# options # => Any non-standard options passed to validates_with
-
# end
-
# end
-
#
-
# To cause a validation error, you must add to the +record+'s errors directly
-
# from within the validators message.
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record.errors.add :base, "This is some custom error message"
-
# record.errors.add :first_name, "This is some complex validation"
-
# # etc...
-
# end
-
# end
-
#
-
# To add behavior to the initialize method, use the following signature:
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def initialize(options)
-
# super
-
# @my_custom_field = options[:field_name] || :first_name
-
# end
-
# end
-
#
-
# The easiest way to add custom validators for validating individual attributes
-
# is with the convenient <tt>ActiveModel::EachValidator</tt>.
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, 'must be Mr., Mrs., or Dr.' unless %w(Mr. Mrs. Dr.).include?(value)
-
# end
-
# end
-
#
-
# This can now be used in combination with the +validates+ method
-
# (see <tt>ActiveModel::Validations::ClassMethods.validates</tt> for more on this).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :title
-
#
-
# validates :title, presence: true
-
# end
-
#
-
# Validator may also define a +setup+ instance method which will get called
-
# with the class that using that validator as its argument. This can be
-
# useful when there are prerequisites such as an +attr_accessor+ being present.
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def setup(klass)
-
# klass.send :attr_accessor, :custom_attribute
-
# end
-
# end
-
#
-
# This setup method is only called when used with validation macros or the
-
# class level <tt>validates_with</tt> method.
-
1
class Validator
-
1
attr_reader :options
-
-
# Returns the kind of the validator.
-
#
-
# PresenceValidator.kind # => :presence
-
# UniquenessValidator.kind # => :uniqueness
-
1
def self.kind
-
5
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
-
end
-
-
# Accepts options that will be made available through the +options+ reader.
-
1
def initialize(options)
-
320
@options = options.freeze
-
end
-
-
# Return the kind for this validator.
-
#
-
# PresenceValidator.new.kind # => :presence
-
# UniquenessValidator.new.kind # => :uniqueness
-
1
def kind
-
5
self.class.kind
-
end
-
-
# Override this method in subclasses with validation logic, adding errors
-
# to the records +errors+ array where necessary.
-
1
def validate(record)
-
raise NotImplementedError, "Subclasses must implement a validate(record) method."
-
end
-
end
-
-
# +EachValidator+ is a validator which iterates through the attributes given
-
# in the options hash invoking the <tt>validate_each</tt> method passing in the
-
# record, attribute and value.
-
#
-
# All Active Model validations are built on top of this validator.
-
1
class EachValidator < Validator #:nodoc:
-
1
attr_reader :attributes
-
-
# Returns a new validator instance. All options will be available via the
-
# +options+ reader, however the <tt>:attributes</tt> option will be removed
-
# and instead be made available through the +attributes+ reader.
-
1
def initialize(options)
-
309
@attributes = Array(options.delete(:attributes))
-
309
raise ArgumentError, ":attributes cannot be blank" if @attributes.empty?
-
308
super
-
308
check_validity!
-
end
-
-
# Performs validation on the supplied record. By default this will call
-
# +validates_each+ to determine validity therefore subclasses should
-
# override +validates_each+ with validation logic.
-
1
def validate(record)
-
518
attributes.each do |attribute|
-
544
value = record.read_attribute_for_validation(attribute)
-
544
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
-
525
validate_each(record, attribute, value)
-
end
-
end
-
-
# Override this method in subclasses with the validation logic, adding
-
# errors to the records +errors+ array where necessary.
-
1
def validate_each(record, attribute, value)
-
raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
-
end
-
-
# Hook method that gets called by the initializer allowing verification
-
# that the arguments supplied are valid. You could for example raise an
-
# +ArgumentError+ when invalid options are supplied.
-
1
def check_validity!
-
end
-
end
-
-
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
-
# and call this block for each attribute being validated. +validates_each+ uses this validator.
-
1
class BlockValidator < EachValidator #:nodoc:
-
1
def initialize(options, &block)
-
2
@block = block
-
2
super
-
end
-
-
1
private
-
-
1
def validate_each(record, attribute, value)
-
8
@block.call(record, attribute, value)
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module VERSION #:nodoc:
-
1
MAJOR = 4
-
1
MINOR = 0
-
1
TINY = 0
-
1
PRE = "beta"
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
#--
-
# Copyright (c) 2005-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'securerandom'
-
1
require "active_support/dependencies/autoload"
-
1
require "active_support/version"
-
1
require "active_support/logger"
-
1
require "active_support/lazy_load_hooks"
-
-
1
module ActiveSupport
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Concern
-
1
autoload :Dependencies
-
1
autoload :DescendantsTracker
-
1
autoload :FileUpdateChecker
-
1
autoload :LogSubscriber
-
1
autoload :Notifications
-
-
1
eager_autoload do
-
1
autoload :BacktraceCleaner
-
1
autoload :BasicObject
-
1
autoload :Benchmarkable
-
1
autoload :Cache
-
1
autoload :Callbacks
-
1
autoload :Configurable
-
1
autoload :Deprecation
-
1
autoload :Gzip
-
1
autoload :Inflector
-
1
autoload :JSON
-
1
autoload :KeyGenerator
-
1
autoload :MessageEncryptor
-
1
autoload :MessageVerifier
-
1
autoload :Multibyte
-
1
autoload :OptionMerger
-
1
autoload :OrderedHash
-
1
autoload :OrderedOptions
-
1
autoload :StringInquirer
-
1
autoload :TaggedLogging
-
1
autoload :XmlMini
-
end
-
-
1
autoload :Rescuable
-
1
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
-
1
autoload :TestCase
-
end
-
-
1
autoload :I18n, "active_support/i18n"
-
1
module ActiveSupport
-
# A class with no predefined methods that behaves similarly to Builder's
-
# BlankSlate. Used for proxy classes.
-
1
class BasicObject < ::BasicObject
-
1
undef_method :==
-
1
undef_method :equal?
-
-
# Let ActiveSupport::BasicObject at least raise exceptions.
-
1
def raise(*args)
-
::Object.send(:raise, *args)
-
end
-
end
-
end
-
1
begin
-
1
require 'builder'
-
rescue LoadError => e
-
$stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
-
raise e
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
module ActiveSupport
-
# Callbacks are code hooks that are run at key points in an object's lifecycle.
-
# The typical use case is to have a base class define a set of callbacks
-
# relevant to the other functionality it supplies, so that subclasses can
-
# install callbacks that enhance or modify the base functionality without
-
# needing to override or redefine methods of the base class.
-
#
-
# Mixing in this module allows you to define the events in the object's
-
# lifecycle that will support callbacks (via +ClassMethods.define_callbacks+),
-
# set the instance methods, procs, or callback objects to be called (via
-
# +ClassMethods.set_callback+), and run the installed callbacks at the
-
# appropriate times (via +run_callbacks+).
-
#
-
# Three kinds of callbacks are supported: before callbacks, run before a
-
# certain event; after callbacks, run after the event; and around callbacks,
-
# blocks that surround the event, triggering it when they yield. Callback code
-
# can be contained in instance methods, procs or lambdas, or callback objects
-
# that respond to certain predetermined methods. See +ClassMethods.set_callback+
-
# for details.
-
#
-
# class Record
-
# include ActiveSupport::Callbacks
-
# define_callbacks :save
-
#
-
# def save
-
# run_callbacks :save do
-
# puts "- save"
-
# end
-
# end
-
# end
-
#
-
# class PersonRecord < Record
-
# set_callback :save, :before, :saving_message
-
# def saving_message
-
# puts "saving..."
-
# end
-
#
-
# set_callback :save, :after do |object|
-
# puts "saved"
-
# end
-
# end
-
#
-
# person = PersonRecord.new
-
# person.save
-
#
-
# Output:
-
# saving...
-
# - save
-
# saved
-
1
module Callbacks
-
1
extend Concern
-
-
1
included do
-
15
extend ActiveSupport::DescendantsTracker
-
end
-
-
# Runs the callbacks for the given event.
-
#
-
# Calls the before and around callbacks in the order they were set, yields
-
# the block (if given one), and then runs the after callbacks in reverse
-
# order.
-
#
-
# If the callback chain was halted, returns +false+. Otherwise returns the
-
# result of the block, or +true+ if no block is given.
-
#
-
# run_callbacks :save do
-
# save
-
# end
-
1
def run_callbacks(kind, &block)
-
2249
runner_name = self.class.__define_callbacks(kind, self)
-
2249
send(runner_name, &block)
-
end
-
-
1
private
-
-
# A hook invoked everytime a before callback is halted.
-
# This can be overridden in AS::Callback implementors in order
-
# to provide better debugging/logging.
-
1
def halted_callback_hook(filter)
-
end
-
-
1
class Callback #:nodoc:#
-
1
@@_callback_sequence = 0
-
-
1
attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
-
-
1
def initialize(chain, filter, kind, options, klass)
-
337
@chain, @kind, @klass = chain, kind, klass
-
337
deprecate_per_key_option(options)
-
337
normalize_options!(options)
-
-
337
@raw_filter, @options = filter, options
-
337
@filter = _compile_filter(filter)
-
337
recompile_options!
-
end
-
-
1
def deprecate_per_key_option(options)
-
337
if options[:per_key]
-
raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
-
end
-
end
-
-
1
def clone(chain, klass)
-
obj = super()
-
obj.chain = chain
-
obj.klass = klass
-
obj.options = @options.dup
-
obj.options[:if] = @options[:if].dup
-
obj.options[:unless] = @options[:unless].dup
-
obj
-
end
-
-
1
def normalize_options!(options)
-
337
options[:if] = Array(options[:if])
-
337
options[:unless] = Array(options[:unless])
-
end
-
-
1
def name
-
302
chain.name
-
end
-
-
1
def next_id
-
475
@@_callback_sequence += 1
-
end
-
-
1
def matches?(_kind, _filter)
-
836
@kind == _kind && @filter == _filter
-
end
-
-
1
def _update_filter(filter_options, new_options)
-
filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
-
filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
-
end
-
-
1
def recompile!(_options)
-
deprecate_per_key_option(_options)
-
_update_filter(self.options, _options)
-
-
recompile_options!
-
end
-
-
# Wraps code with filter
-
1
def apply(code)
-
350
case @kind
-
when :before
-
<<-RUBY_EVAL
-
337
if !halted && #{@compiled_options}
-
# This double assignment is to prevent warnings in 1.9.3 as
-
# the `result` variable is not always used except if the
-
# terminator code refers to it.
-
result = result = #{@filter}
-
halted = (#{chain.config[:terminator]})
-
if halted
-
halted_callback_hook(#{@raw_filter.inspect.inspect})
-
end
-
end
-
#{code}
-
RUBY_EVAL
-
when :after
-
<<-RUBY_EVAL
-
12
#{code}
-
if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
-
#{@filter}
-
end
-
RUBY_EVAL
-
when :around
-
1
name = define_conditional_callback
-
<<-RUBY_EVAL
-
1
#{name}(halted) do
-
#{code}
-
value
-
end
-
RUBY_EVAL
-
end
-
end
-
-
1
private
-
-
# Compile around filters with conditions into proxy methods
-
# that contain the conditions.
-
#
-
# For `set_callback :save, :around, :filter_name, if: :condition':
-
#
-
# def _conditional_callback_save_17
-
# if condition
-
# filter_name do
-
# yield self
-
# end
-
# else
-
# yield self
-
# end
-
# end
-
1
def define_conditional_callback
-
1
name = "_conditional_callback_#{@kind}_#{next_id}"
-
1
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}(halted)
-
if #{@compiled_options} && !halted
-
#{@filter} do
-
yield self
-
end
-
else
-
yield self
-
end
-
end
-
RUBY_EVAL
-
1
name
-
end
-
-
# Options support the same options as filters themselves (and support
-
# symbols, string, procs, and objects), so compile a conditional
-
# expression based on the options.
-
1
def recompile_options!
-
337
conditions = ["true"]
-
-
337
unless options[:if].empty?
-
42
conditions << Array(_compile_filter(options[:if]))
-
end
-
-
337
unless options[:unless].empty?
-
52
conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
-
end
-
-
337
@compiled_options = conditions.flatten.join(" && ")
-
end
-
-
# Filters support:
-
#
-
# Arrays:: Used in conditions. This is used to specify
-
# multiple conditions. Used internally to
-
# merge conditions from skip_* filters.
-
# Symbols:: A method to call.
-
# Strings:: Some content to evaluate.
-
# Procs:: A proc to call with the object.
-
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
-
#
-
# All of these objects are compiled into methods and handled
-
# the same after this point:
-
#
-
# Arrays:: Merged together into a single filter.
-
# Symbols:: Already methods.
-
# Strings:: class_eval'ed into methods.
-
# Procs:: define_method'ed into methods.
-
# Objects::
-
# a method is created that calls the before_foo method
-
# on the object.
-
1
def _compile_filter(filter)
-
474
method_name = "_callback_#{@kind}_#{next_id}"
-
474
case filter
-
when Array
-
137
filter.map {|f| _compile_filter(f)}
-
when Symbol
-
26
filter
-
when String
-
26
"(#{filter})"
-
when Proc
-
52
@klass.send(:define_method, method_name, &filter)
-
52
return method_name if filter.arity <= 0
-
-
7
method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
-
else
-
885
@klass.send(:define_method, "#{method_name}_object") { filter }
-
-
302
_normalize_legacy_filter(kind, filter)
-
302
scopes = Array(chain.config[:scope])
-
605
method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
-
-
302
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{method_name}(&blk)
-
#{method_name}_object.send(:#{method_to_call}, self, &blk)
-
end
-
RUBY_EVAL
-
-
302
method_name
-
end
-
end
-
-
1
def _normalize_legacy_filter(kind, filter)
-
302
if !filter.respond_to?(kind) && filter.respond_to?(:filter)
-
message = "Filter object with #filter method is deprecated. Define method corresponding " \
-
"to filter type (#before, #after or #around)."
-
ActiveSupport::Deprecation.warn message
-
filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{kind}(context, &block) filter(context, &block) end
-
RUBY_EVAL
-
302
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
-
message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
-
ActiveSupport::Deprecation.warn message
-
def filter.around(context)
-
should_continue = before(context)
-
yield if should_continue
-
after(context)
-
end
-
end
-
end
-
end
-
-
# An Array with a compile method.
-
1
class CallbackChain < Array #:nodoc:#
-
1
attr_reader :name, :config
-
-
1
def initialize(name, config)
-
25
@name = name
-
25
@config = {
-
:terminator => "false",
-
:scope => [ :kind ]
-
}.merge(config)
-
end
-
-
1
def compile
-
377
method = []
-
377
method << "value = nil"
-
377
method << "halted = false"
-
-
377
callbacks = "value = !halted && (!block_given? || yield)"
-
377
reverse_each do |callback|
-
350
callbacks = callback.apply(callbacks)
-
end
-
377
method << callbacks
-
-
377
method << "value"
-
377
method.join("\n")
-
end
-
-
end
-
-
1
module ClassMethods
-
-
# This method defines callback chain method for the given kind
-
# if it was not yet defined.
-
# This generated method plays caching role.
-
1
def __define_callbacks(kind, object) #:nodoc:
-
2249
name = __callback_runner_name(kind)
-
2249
unless object.respond_to?(name, true)
-
377
str = object.send("_#{kind}_callbacks").compile
-
377
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}() #{str} end
-
protected :#{name}
-
RUBY_EVAL
-
end
-
2249
name
-
end
-
-
1
def __reset_runner(symbol)
-
1796
name = __callback_runner_name(symbol)
-
1796
undef_method(name) if method_defined?(name)
-
end
-
-
1
def __callback_runner_name(kind)
-
4045
"_run__#{self.name.hash.abs}__#{kind}__callbacks"
-
end
-
-
# This is used internally to append, prepend and skip callbacks to the
-
# CallbackChain.
-
1
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
-
336
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
-
336
options = filters.last.is_a?(Hash) ? filters.pop : {}
-
336
filters.unshift(block) if block
-
-
336
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
-
631
chain = target.send("_#{name}_callbacks")
-
631
yield target, chain.dup, type, filters, options
-
631
target.__reset_runner(name)
-
end
-
end
-
-
# Install a callback for the given event.
-
#
-
# set_callback :save, :before, :before_meth
-
# set_callback :save, :after, :after_meth, if: :condition
-
# set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
-
#
-
# The second arguments indicates whether the callback is to be run +:before+,
-
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
-
# means the first example above can also be written as:
-
#
-
# set_callback :save, :before_meth
-
#
-
# The callback can specified as a symbol naming an instance method; as a
-
# proc, lambda, or block; as a string to be instance evaluated; or as an
-
# object that responds to a certain method determined by the <tt>:scope</tt>
-
# argument to +define_callback+.
-
#
-
# If a proc, lambda, or block is given, its body is evaluated in the context
-
# of the current object. It can also optionally accept the current object as
-
# an argument.
-
#
-
# Before and around callbacks are called in the order that they are set;
-
# after callbacks are called in the reverse order.
-
#
-
# Around callbacks can access the return value from the event, if it
-
# wasn't halted, from the +yield+ call.
-
#
-
# ===== Options
-
#
-
# * <tt>:if</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +true+ value.
-
# * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +false+ value.
-
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
-
# existing chain rather than appended.
-
1
def set_callback(name, *filter_list, &block)
-
336
mapped = nil
-
-
336
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
mapped ||= filters.map do |filter|
-
337
Callback.new(chain, filter, type, options.dup, self)
-
631
end
-
-
631
filters.each do |filter|
-
1468
chain.delete_if {|c| c.matches?(type, filter) }
-
end
-
-
631
options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
-
-
631
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
-
# <tt>:unless</tt> options may be passed in order to control when the
-
# callback is skipped.
-
#
-
# class Writer < Person
-
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
-
# end
-
1
def skip_callback(name, *filter_list, &block)
-
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
filters.each do |filter|
-
filter = chain.find {|c| c.matches?(type, filter) }
-
-
if filter && options.any?
-
new_filter = filter.clone(chain, self)
-
chain.insert(chain.index(filter), new_filter)
-
new_filter.recompile!(options)
-
end
-
-
chain.delete(filter)
-
end
-
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Remove all set callbacks for the given event.
-
1
def reset_callbacks(symbol)
-
611
callbacks = send("_#{symbol}_callbacks")
-
-
611
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
-
554
chain = target.send("_#{symbol}_callbacks").dup
-
849
callbacks.each { |c| chain.delete(c) }
-
554
target.send("_#{symbol}_callbacks=", chain)
-
554
target.__reset_runner(symbol)
-
end
-
-
611
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
-
-
611
__reset_runner(symbol)
-
end
-
-
# Define sets of events in the object lifecycle that support callbacks.
-
#
-
# define_callbacks :validate
-
# define_callbacks :initialize, :save, :destroy
-
#
-
# ===== Options
-
#
-
# * <tt>:terminator</tt> - Determines when a before filter will halt the
-
# callback chain, preventing following callbacks from being called and
-
# the event from being triggered. This is a string to be eval'ed. The
-
# result of the callback is available in the +result+ variable.
-
#
-
# define_callbacks :validate, terminator: 'result == false'
-
#
-
# In this example, if any before validate callbacks returns +false+,
-
# other callbacks are not executed. Defaults to +false+, meaning no value
-
# halts the chain.
-
#
-
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
-
# callbacks should be terminated by the <tt>:terminator</tt> option. By
-
# default after callbacks executed no matter if callback chain was
-
# terminated or not. Option makes sense only when <tt>:terminator</tt>
-
# option is specified.
-
#
-
# * <tt>:scope</tt> - Indicates which methods should be executed when an
-
# object is used as a callback.
-
#
-
# class Audit
-
# def before(caller)
-
# puts 'Audit: before is called'
-
# end
-
#
-
# def before_save(caller)
-
# puts 'Audit: before_save is called'
-
# end
-
# end
-
#
-
# class Account
-
# include ActiveSupport::Callbacks
-
#
-
# define_callbacks :save
-
# set_callback :save, :before, Audit.new
-
#
-
# def save
-
# run_callbacks :save do
-
# puts 'save in main'
-
# end
-
# end
-
# end
-
#
-
# In the above case whenever you save an account the method
-
# <tt>Audit#before</tt> will be called. On the other hand
-
#
-
# define_callbacks :save, scope: [:kind, :name]
-
#
-
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
-
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
-
# case "kind" is "before" and "name" is "save". In this context +:kind+
-
# and +:name+ have special meanings: +:kind+ refers to the kind of
-
# callback (before/after/around) and +:name+ refers to the method on
-
# which callbacks are being defined.
-
#
-
# A declaration like
-
#
-
# define_callbacks :save, scope: [:name]
-
#
-
# would call <tt>Audit#save</tt>.
-
1
def define_callbacks(*callbacks)
-
24
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
-
24
callbacks.each do |callback|
-
25
class_attribute "_#{callback}_callbacks"
-
25
send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# A typical module looks like this:
-
#
-
# module M
-
# def self.included(base)
-
# base.extend ClassMethods
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
-
# written as:
-
#
-
# require 'active_support/concern'
-
#
-
# module M
-
# extend ActiveSupport::Concern
-
#
-
# included do
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
-
# and a +Bar+ module which depends on the former, we would typically write the
-
# following:
-
#
-
# module Foo
-
# def self.included(base)
-
# base.class_eval do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Foo # We need to include this dependency for Bar
-
# include Bar # Bar is the module that Host really needs
-
# end
-
#
-
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
-
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
-
#
-
# module Bar
-
# include Foo
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar
-
# end
-
#
-
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
-
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
-
# module dependencies are properly resolved:
-
#
-
# require 'active_support/concern'
-
#
-
# module Foo
-
# extend ActiveSupport::Concern
-
# included do
-
# class_eval do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# extend ActiveSupport::Concern
-
# include Foo
-
#
-
# included do
-
# self.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar # works, Bar takes care now of its dependencies
-
# end
-
1
module Concern
-
1
def self.extended(base) #:nodoc:
-
13
base.instance_variable_set("@_dependencies", [])
-
end
-
-
1
def append_features(base)
-
60
if base.instance_variable_defined?("@_dependencies")
-
1
base.instance_variable_get("@_dependencies") << self
-
1
return false
-
else
-
59
return false if base < self
-
54
@_dependencies.each { |dep| base.send(:include, dep) }
-
53
super
-
53
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
-
53
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
-
end
-
end
-
-
1
def included(base = nil, &block)
-
69
if base.nil?
-
9
@_included_block = block
-
else
-
60
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
-
1
class Array
-
# Converts the array to a comma-separated sentence where the last element is
-
# joined by the connector word.
-
#
-
# You can pass the following options to change the default behaviour. If you
-
# pass an option key that doesn't exist in the list below, it will raise an
-
# <tt>ArgumentError</tt>.
-
#
-
# Options:
-
#
-
# * <tt>:words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two or more elements (default: ", ").
-
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two elements (default: " and ").
-
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
-
# in arrays with three or more elements (default: ", and ").
-
# * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
-
# the connector options defined on the 'support.array' namespace in the
-
# corresponding dictionary file.
-
#
-
# [].to_sentence # => ""
-
# ['one'].to_sentence # => "one"
-
# ['one', 'two'].to_sentence # => "one and two"
-
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
-
#
-
# ['one', 'two'].to_sentence(passing: 'invalid option')
-
# # => ArgumentError: Unknown key :passing
-
#
-
# ['one', 'two'].to_sentence(two_words_connector: '-')
-
# # => "one-two"
-
#
-
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
-
# # => "one or two or at least three"
-
#
-
# Examples using <tt>:locale</tt> option:
-
#
-
# # Given this locale dictionary:
-
# #
-
# # es:
-
# # support:
-
# # array:
-
# # words_connector: " o "
-
# # two_words_connector: " y "
-
# # last_word_connector: " o al menos "
-
#
-
# ['uno', 'dos'].to_sentence(locale: :es)
-
# # => "uno y dos"
-
#
-
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
-
# # => "uno o dos o al menos tres"
-
1
def to_sentence(options = {})
-
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
-
-
default_connectors = {
-
:words_connector => ', ',
-
:two_words_connector => ' and ',
-
:last_word_connector => ', and '
-
}
-
if defined?(I18n)
-
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
-
default_connectors.merge!(i18n_connectors)
-
end
-
options = default_connectors.merge!(options)
-
-
case length
-
when 0
-
''
-
when 1
-
self[0].to_s.dup
-
when 2
-
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
-
else
-
"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
-
end
-
end
-
-
# Converts a collection of elements into a formatted string by calling
-
# <tt>to_s</tt> on all elements and joining them. Having this model:
-
#
-
# class Blog < ActiveRecord::Base
-
# def to_s
-
# title
-
# end
-
# end
-
#
-
# Blog.all.map(&:title) #=> ["First Post", "Second Post", "Third post"]
-
#
-
# <tt>to_formatted_s</tt> shows us:
-
#
-
# Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
-
#
-
# Adding in the <tt>:db</tt> argument as the format yields a comma separated
-
# id list:
-
#
-
# Blog.all.to_formatted_s(:db) # => "1,2,3"
-
1
def to_formatted_s(format = :default)
-
case format
-
when :db
-
if empty?
-
'null'
-
else
-
collect { |element| element.id }.join(',')
-
end
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns a string that represents the array in XML by invoking +to_xml+
-
# on each element. Active Record collections delegate their representation
-
# in XML to this method.
-
#
-
# All elements are expected to respond to +to_xml+, if any of them does
-
# not then an exception is raised.
-
#
-
# The root node reflects the class name of the first element in plural
-
# if all elements belong to the same type and that's not Hash:
-
#
-
# customer.projects.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array">
-
# <project>
-
# <amount type="decimal">20000.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-09</deal-date>
-
# ...
-
# </project>
-
# <project>
-
# <amount type="decimal">57230.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-15</deal-date>
-
# ...
-
# </project>
-
# </projects>
-
#
-
# Otherwise the root element is "objects":
-
#
-
# [{ foo: 1, bar: 2}, { baz: 3}].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <objects type="array">
-
# <object>
-
# <bar type="integer">2</bar>
-
# <foo type="integer">1</foo>
-
# </object>
-
# <object>
-
# <baz type="integer">3</baz>
-
# </object>
-
# </objects>
-
#
-
# If the collection is empty the root element is "nil-classes" by default:
-
#
-
# [].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <nil-classes type="array"/>
-
#
-
# To ensure a meaningful root element use the <tt>:root</tt> option:
-
#
-
# customer_with_no_projects.projects.to_xml(root: 'projects')
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array"/>
-
#
-
# By default name of the node for the children of root is <tt>root.singularize</tt>.
-
# You can change it with the <tt>:children</tt> option.
-
#
-
# The +options+ hash is passed downwards:
-
#
-
# Message.all.to_xml(skip_types: true)
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <messages>
-
# <message>
-
# <created-at>2008-03-07T09:58:18+01:00</created-at>
-
# <id>1</id>
-
# <name>1</name>
-
# <updated-at>2008-03-07T09:58:18+01:00</updated-at>
-
# <user-id>1</user-id>
-
# </message>
-
# </messages>
-
#
-
1
def to_xml(options = {})
-
2
require 'active_support/builder' unless defined?(Builder)
-
-
2
options = options.dup
-
2
options[:indent] ||= 2
-
2
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
-
2
options[:root] ||= \
-
if first.class != Hash && all? { |e| e.is_a?(first.class) }
-
underscored = ActiveSupport::Inflector.underscore(first.class.name)
-
ActiveSupport::Inflector.pluralize(underscored).tr('/', '_')
-
else
-
'objects'
-
end
-
-
2
builder = options[:builder]
-
2
builder.instruct! unless options.delete(:skip_instruct)
-
-
2
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
2
children = options.delete(:children) || root.singularize
-
2
attributes = options[:skip_types] ? {} : { type: 'array' }
-
-
2
if empty?
-
builder.tag!(root, attributes)
-
else
-
2
builder.tag!(root, attributes) do
-
6
each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
-
2
yield builder if block_given?
-
end
-
end
-
end
-
end
-
1
class Hash
-
# By default, only instances of Hash itself are extractable.
-
# Subclasses of Hash may implement this method and return
-
# true to declare themselves as extractable. If a Hash
-
# is extractable, Array#extract_options! pops it from
-
# the Array when it is the last element of the Array.
-
1
def extractable_options?
-
908
instance_of?(Hash)
-
end
-
end
-
-
1
class Array
-
# Extracts options from a set of arguments. Removes and returns the last
-
# element in the array if it's a hash, otherwise returns a blank hash.
-
#
-
# def options(*args)
-
# args.extract_options!
-
# end
-
#
-
# options(1, 2) # => {}
-
# options(1, 2, a: :b) # => {:a=>:b}
-
1
def extract_options!
-
1004
if last.is_a?(Hash) && last.extractable_options?
-
908
pop
-
else
-
96
{}
-
end
-
end
-
end
-
1
class Array
-
# The human way of thinking about adding stuff to the end of a list is with append
-
1
alias_method :append, :<<
-
-
# The human way of thinking about adding stuff to the beginning of a list is with prepend
-
1
alias_method :prepend, :unshift
-
end
-
1
class Array
-
# Wraps its argument in an array unless it is already an array (or array-like).
-
#
-
# Specifically:
-
#
-
# * If the argument is +nil+ an empty list is returned.
-
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
-
# * Otherwise, returns an array with the argument as its single element.
-
#
-
# Array.wrap(nil) # => []
-
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
-
# Array.wrap(0) # => [0]
-
#
-
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
-
#
-
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
-
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
-
# such a +nil+ right away.
-
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
-
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
-
# * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
-
#
-
# The last point is particularly worth comparing for some enumerables:
-
#
-
# Array(foo: :bar) # => [[:foo, :bar]]
-
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
-
#
-
# There's also a related idiom that uses the splat operator:
-
#
-
# [*object]
-
#
-
# which for +nil+ returns <tt>[nil]</tt> (Ruby 1.8.7) or <tt>[]</tt> (Ruby
-
# 1.9), and calls to <tt>Array(object)</tt> otherwise.
-
#
-
# Thus, in this case the behavior may be different for +nil+, and the differences with
-
# <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s.
-
1
def self.wrap(object)
-
if object.nil?
-
[]
-
elsif object.respond_to?(:to_ary)
-
object.to_ary || [object]
-
else
-
[object]
-
end
-
end
-
end
-
1
require 'bigdecimal'
-
1
require 'yaml'
-
-
1
class BigDecimal
-
1
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
-
-
1
def encode_with(coder)
-
string = to_s
-
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
-
end
-
-
# Backport this method if it doesn't exist
-
1
unless method_defined?(:to_d)
-
1
def to_d
-
self
-
end
-
end
-
-
1
DEFAULT_STRING_FORMAT = 'F'
-
1
def to_formatted_s(*args)
-
2
if args[0].is_a?(Symbol)
-
super
-
else
-
2
format = args[0] || DEFAULT_STRING_FORMAT
-
2
_original_to_s(format)
-
end
-
end
-
1
alias_method :_original_to_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Class
-
# Declare a class-level attribute whose value is inheritable by subclasses.
-
# Subclasses can change their own value and it will not impact parent class.
-
#
-
# class Base
-
# class_attribute :setting
-
# end
-
#
-
# class Subclass < Base
-
# end
-
#
-
# Base.setting = true
-
# Subclass.setting # => true
-
# Subclass.setting = false
-
# Subclass.setting # => false
-
# Base.setting # => true
-
#
-
# In the above case as long as Subclass does not assign a value to setting
-
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
-
# would read value assigned to parent class. Once Subclass assigns a value then
-
# the value assigned by Subclass would be returned.
-
#
-
# This matches normal Ruby method inheritance: think of writing an attribute
-
# on a subclass as overriding the reader method. However, you need to be aware
-
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
-
# In such cases, you don't want to do changes in places but use setters:
-
#
-
# Base.setting = []
-
# Base.setting # => []
-
# Subclass.setting # => []
-
#
-
# # Appending in child changes both parent and child because it is the same object:
-
# Subclass.setting << :foo
-
# Base.setting # => [:foo]
-
# Subclass.setting # => [:foo]
-
#
-
# # Use setters to not propagate changes:
-
# Base.setting = []
-
# Subclass.setting += [:foo]
-
# Base.setting # => []
-
# Subclass.setting # => [:foo]
-
#
-
# For convenience, a query method is defined as well:
-
#
-
# Subclass.setting? # => false
-
#
-
# Instances may overwrite the class value in the same way:
-
#
-
# Base.setting = true
-
# object = Base.new
-
# object.setting # => true
-
# object.setting = false
-
# object.setting # => false
-
# Base.setting # => true
-
#
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# object.setting # => NoMethodError
-
# object.setting? # => NoMethodError
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
#
-
# object.setting = false # => NoMethodError
-
#
-
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
-
1
def class_attribute(*attrs)
-
45
options = attrs.extract_options!
-
45
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
-
45
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
-
-
45
attrs.each do |name|
-
52
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def self.#{name}() nil end
-
def self.#{name}?() !!#{name} end
-
-
def self.#{name}=(val)
-
singleton_class.class_eval do
-
remove_possible_method(:#{name})
-
define_method(:#{name}) { val }
-
end
-
-
if singleton_class?
-
class_eval do
-
remove_possible_method(:#{name})
-
def #{name}
-
defined?(@#{name}) ? @#{name} : singleton_class.#{name}
-
end
-
end
-
end
-
val
-
end
-
-
if instance_reader
-
remove_possible_method :#{name}
-
def #{name}
-
defined?(@#{name}) ? @#{name} : self.class.#{name}
-
end
-
-
def #{name}?
-
!!#{name}
-
end
-
end
-
RUBY
-
-
52
attr_writer name if instance_writer
-
end
-
end
-
-
1
private
-
1
def singleton_class?
-
1886
ancestors.first != self
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
# Extends the class object with class and instance accessors for class attributes,
-
# just like the native attr* accessors for instance attributes.
-
1
class Class
-
# Defines a class attribute if it's not defined and creates a reader method that
-
# returns the attribute value.
-
#
-
# class Person
-
# cattr_reader :hair_colors
-
# end
-
#
-
# Person.class_variable_set("@@hair_colors", [:brown, :black])
-
# Person.hair_colors # => [:brown, :black]
-
# Person.new.hair_colors # => [:brown, :black]
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class Person
-
# cattr_reader :"1_Badname "
-
# end
-
# # => NameError: invalid attribute name
-
#
-
# If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
-
# or <tt>instance_accessor: false</tt>.
-
#
-
# class Person
-
# cattr_reader :hair_colors, instance_reader: false
-
# end
-
#
-
# Person.new.hair_colors # => NoMethodError
-
1
def cattr_reader(*syms)
-
options = syms.extract_options!
-
syms.each do |sym|
-
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
unless defined? @@#{sym}
-
@@#{sym} = nil
-
end
-
-
def self.#{sym}
-
@@#{sym}
-
end
-
EOS
-
-
unless options[:instance_reader] == false || options[:instance_accessor] == false
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}
-
@@#{sym}
-
end
-
EOS
-
end
-
end
-
end
-
-
# Defines a class attribute if it's not defined and creates a writer method to allow
-
# assignment to the attribute.
-
#
-
# class Person
-
# cattr_writer :hair_colors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black]
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
-
# Person.new.hair_colors = [:blonde, :red]
-
# Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class Person
-
# cattr_writer :"1_Badname "
-
# end
-
# # => NameError: invalid attribute name
-
#
-
# If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
-
# or <tt>instance_accessor: false</tt>.
-
#
-
# class Person
-
# cattr_writer :hair_colors, instance_writer: false
-
# end
-
#
-
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
-
#
-
# Also, you can pass a block to set up the attribute with a default value.
-
#
-
# class Person
-
# cattr_writer :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
-
1
def cattr_writer(*syms)
-
options = syms.extract_options!
-
syms.each do |sym|
-
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
unless defined? @@#{sym}
-
@@#{sym} = nil
-
end
-
-
def self.#{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
-
unless options[:instance_writer] == false || options[:instance_accessor] == false
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
end
-
send("#{sym}=", yield) if block_given?
-
end
-
end
-
-
# Defines both class and instance accessors for class attributes.
-
#
-
# class Person
-
# cattr_accessor :hair_colors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black, :blonde, :red]
-
# Person.hair_colors # => [:brown, :black, :blonde, :red]
-
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-
#
-
# If a subclass changes the value then that would also change the value for
-
# parent class. Similarly if parent class changes the value then that would
-
# change the value of subclasses too.
-
#
-
# class Male < Person
-
# end
-
#
-
# Male.hair_colors << :blue
-
# Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# class Person
-
# cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
-
#
-
# class Person
-
# cattr_accessor :hair_colors, instance_accessor: false
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Also you can pass a block to set up the attribute with a default value.
-
#
-
# class Person
-
# cattr_accessor :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
-
1
def cattr_accessor(*syms, &blk)
-
cattr_reader(*syms)
-
cattr_writer(*syms, &blk)
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Date
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Date
-
1
include DateAndTime::Calculations
-
-
1
@beginning_of_week_default = nil
-
-
1
class << self
-
1
attr_accessor :beginning_of_week_default
-
-
# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
-
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
-
# If no config.beginning_of_week was specified, returns :monday.
-
1
def beginning_of_week
-
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
-
end
-
-
# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
-
#
-
# This method accepts any of the following day symbols:
-
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
-
1
def beginning_of_week=(week_start)
-
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
-
end
-
-
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
-
1
def find_beginning_of_week!(week_start)
-
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
-
week_start
-
end
-
-
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
-
1
def yesterday
-
::Date.current.yesterday
-
end
-
-
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
-
1
def tomorrow
-
::Date.current.tomorrow
-
end
-
-
# Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
-
1
def current
-
::Time.zone ? ::Time.zone.today : ::Date.today
-
end
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then subtracts the specified number of seconds.
-
1
def ago(seconds)
-
to_time_in_current_zone.since(-seconds)
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then adds the specified number of seconds
-
1
def since(seconds)
-
to_time_in_current_zone.since(seconds)
-
end
-
1
alias :in :since
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
1
def beginning_of_day
-
to_time_in_current_zone
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
-
1
def end_of_day
-
to_time_in_current_zone.end_of_day
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
plus_with_duration(-other)
-
else
-
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
-
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
-
1
def advance(options)
-
options = options.dup
-
d = self
-
d = d >> options.delete(:years) * 12 if options[:years]
-
d = d >> options.delete(:months) if options[:months]
-
d = d + options.delete(:weeks) * 7 if options[:weeks]
-
d = d + options.delete(:days) if options[:days]
-
d
-
end
-
-
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
-
# The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
-
#
-
# Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
-
# Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
-
1
def change(options)
-
::Date.new(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day)
-
)
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Date
-
1
DATE_FORMATS = {
-
:short => '%e %b',
-
:long => '%B %e, %Y',
-
:db => '%Y-%m-%d',
-
:number => '%Y%m%d',
-
:long_ordinal => lambda { |date|
-
day_format = ActiveSupport::Inflector.ordinalize(date.day)
-
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
-
},
-
:rfc822 => '%e %b %Y'
-
}
-
-
# Ruby 1.9 has Date#to_time which converts to localtime only.
-
1
remove_possible_method :to_time
-
-
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
-
1
remove_possible_method :xmlschema
-
-
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_formatted_s(:db) # => "2007-11-10"
-
# date.to_s(:db) # => "2007-11-10"
-
#
-
# date.to_formatted_s(:short) # => "10 Nov"
-
# date.to_formatted_s(:long) # => "November 10, 2007"
-
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
-
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
-
#
-
# == Adding your own time formats to to_formatted_s
-
# You can add your own formats to the Date::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a date argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Date::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = DATE_FORMATS[format]
-
if formatter.respond_to?(:call)
-
formatter.call(self).to_s
-
else
-
strftime(formatter)
-
end
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
-
1
def readable_inspect
-
strftime('%a, %d %b %Y')
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
-
# The timezone can be either :local or :utc (default :local).
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_time # => Sat Nov 10 00:00:00 0800 2007
-
# date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
-
#
-
# date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
-
1
def to_time(form = :local)
-
::Time.send("#{form}_time", year, month, day)
-
end
-
-
1
def xmlschema
-
to_time_in_current_zone.xmlschema
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class Date
-
# Converts Date to a TimeWithZone in the current zone if <tt>Time.zone</tt> or
-
# <tt>Time.zone_default</tt> is set, otherwise converts Date to a Time via
-
# Date#to_time.
-
1
def to_time_in_current_zone
-
if ::Time.zone
-
::Time.zone.local(year, month, day)
-
else
-
to_time
-
end
-
end
-
end
-
1
module DateAndTime
-
1
module Calculations
-
1
DAYS_INTO_WEEK = {
-
:monday => 0,
-
:tuesday => 1,
-
:wednesday => 2,
-
:thursday => 3,
-
:friday => 4,
-
:saturday => 5,
-
:sunday => 6
-
}
-
-
# Returns a new date/time representing yesterday.
-
1
def yesterday
-
advance(:days => -1)
-
end
-
-
# Returns a new date/time representing tomorrow.
-
1
def tomorrow
-
advance(:days => 1)
-
end
-
-
# Returns true if the date/time is today.
-
1
def today?
-
to_date == ::Date.current
-
end
-
-
# Returns true if the date/time is in the past.
-
1
def past?
-
self < self.class.current
-
end
-
-
# Returns true if the date/time is in the future.
-
1
def future?
-
self > self.class.current
-
end
-
-
# Returns a new date/time the specified number of days ago.
-
1
def days_ago(days)
-
advance(:days => -days)
-
end
-
-
# Returns a new date/time the specified number of days in the future.
-
1
def days_since(days)
-
advance(:days => days)
-
end
-
-
# Returns a new date/time the specified number of weeks ago.
-
1
def weeks_ago(weeks)
-
advance(:weeks => -weeks)
-
end
-
-
# Returns a new date/time the specified number of weeks in the future.
-
1
def weeks_since(weeks)
-
advance(:weeks => weeks)
-
end
-
-
# Returns a new date/time the specified number of months ago.
-
1
def months_ago(months)
-
advance(:months => -months)
-
end
-
-
# Returns a new date/time the specified number of months in the future.
-
1
def months_since(months)
-
advance(:months => months)
-
end
-
-
# Returns a new date/time the specified number of years ago.
-
1
def years_ago(years)
-
advance(:years => -years)
-
end
-
-
# Returns a new date/time the specified number of years in the future.
-
1
def years_since(years)
-
advance(:years => years)
-
end
-
-
# Returns a new date/time at the start of the month.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_month
-
first_hour{ change(:day => 1) }
-
end
-
1
alias :at_beginning_of_month :beginning_of_month
-
-
# Returns a new date/time at the start of the quarter.
-
# Example: 1st January, 1st July, 1st October.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_quarter
-
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
-
beginning_of_month.change(:month => first_quarter_month)
-
end
-
1
alias :at_beginning_of_quarter :beginning_of_quarter
-
-
# Returns a new date/time at the end of the quarter.
-
# Example: 31st March, 30th June, 30th September.
-
# DateTIme objects will have a time set to 23:59:59.
-
1
def end_of_quarter
-
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
-
beginning_of_month.change(:month => last_quarter_month).end_of_month
-
end
-
1
alias :at_end_of_quarter :end_of_quarter
-
-
# Return a new date/time at the beginning of the year.
-
# Example: 1st January.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_year
-
change(:month => 1).beginning_of_month
-
end
-
1
alias :at_beginning_of_year :beginning_of_year
-
-
# Returns a new date/time representing the given day in the next week.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 0:00.
-
1
def next_week(start_day = Date.beginning_of_week)
-
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
-
end
-
-
# Short-hand for months_since(1).
-
1
def next_month
-
months_since(1)
-
end
-
-
# Short-hand for months_since(3)
-
1
def next_quarter
-
months_since(3)
-
end
-
-
# Short-hand for years_since(1).
-
1
def next_year
-
years_since(1)
-
end
-
-
# Returns a new date/time representing the given day in the previous week.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 0:00.
-
1
def prev_week(start_day = Date.beginning_of_week)
-
first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
-
end
-
1
alias_method :last_week, :prev_week
-
-
# Short-hand for months_ago(1).
-
1
def prev_month
-
months_ago(1)
-
end
-
1
alias_method :last_month, :prev_month
-
-
# Short-hand for months_ago(3).
-
1
def prev_quarter
-
months_ago(3)
-
end
-
1
alias_method :last_quarter, :prev_quarter
-
-
# Short-hand for years_ago(1).
-
1
def prev_year
-
years_ago(1)
-
end
-
1
alias_method :last_year, :prev_year
-
-
# Returns the number of days to the start of the week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
1
def days_to_week_start(start_day = Date.beginning_of_week)
-
start_day_number = DAYS_INTO_WEEK[start_day]
-
current_day_number = wday != 0 ? wday - 1 : 6
-
(current_day_number - start_day_number) % 7
-
end
-
-
# Returns a new date/time representing the start of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def beginning_of_week(start_day = Date.beginning_of_week)
-
result = days_ago(days_to_week_start(start_day))
-
acts_like?(:time) ? result.midnight : result
-
end
-
1
alias :at_beginning_of_week :beginning_of_week
-
-
# Returns Monday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def monday
-
beginning_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 23:59:59.
-
1
def end_of_week(start_day = Date.beginning_of_week)
-
last_hour{ days_since(6 - days_to_week_start(start_day)) }
-
end
-
1
alias :at_end_of_week :end_of_week
-
-
# Returns Sunday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 23:59:59.
-
1
def sunday
-
end_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of the month.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_month
-
last_day = ::Time.days_in_month(month, year)
-
last_hour{ days_since(last_day - day) }
-
end
-
1
alias :at_end_of_month :end_of_month
-
-
# Returns a new date/time representing the end of the year.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_year
-
change(:month => 12).end_of_month
-
end
-
1
alias :at_end_of_year :end_of_year
-
-
1
private
-
-
1
def first_hour
-
result = yield
-
acts_like?(:time) ? result.change(:hour => 0) : result
-
end
-
-
1
def last_hour
-
result = yield
-
acts_like?(:time) ? result.end_of_day : result
-
end
-
-
1
def days_span(day)
-
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class DateTime
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
class DateTime
-
1
class << self
-
# *DEPRECATED*: Use +DateTime.civil_from_format+ directly.
-
1
def local_offset
-
ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.'
-
-
::Time.local(2012).utc_offset.to_r / 86400
-
end
-
-
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
-
# <tt>config.time_zone</tt> are set, otherwise returns
-
# <tt>Time.now.to_datetime</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
-
end
-
end
-
-
# Tells whether the DateTime object's datetime lies in the past.
-
1
def past?
-
self < ::DateTime.current
-
end
-
-
# Tells whether the DateTime object's datetime lies in the future.
-
1
def future?
-
self > ::DateTime.current
-
end
-
-
# Seconds since midnight: DateTime.now.seconds_since_midnight.
-
1
def seconds_since_midnight
-
sec + (min * 60) + (hour * 3600)
-
end
-
-
# Returns a new DateTime where one or more of the elements have been changed
-
# according to the +options+ parameter. The time options (<tt>:hour</tt>,
-
# <tt>:minute</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
-
# passed, then minute and sec is set to 0. If the hour and minute is passed,
-
# then sec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
-
# <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
-
#
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
::DateTime.civil(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day),
-
options.fetch(:hour, hour),
-
options.fetch(:min, options[:hour] ? 0 : min),
-
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec),
-
options.fetch(:offset, offset),
-
options.fetch(:start, start)
-
)
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
d = to_date.advance(options)
-
datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
if seconds_to_advance.zero?
-
datetime_advanced_by_date
-
else
-
datetime_advanced_by_date.since seconds_to_advance
-
end
-
end
-
-
# Returns a new DateTime representing the time a number of seconds ago.
-
# Do not use this method in combination with x.months, use months_ago instead!
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new DateTime representing the time a number of seconds since the
-
# instance time. Do not use this method in combination with x.months, use
-
# months_since instead!
-
1
def since(seconds)
-
self + Rational(seconds.round, 86400)
-
end
-
1
alias :in :since
-
-
# Returns a new DateTime representing the start of the day (0:00).
-
1
def beginning_of_day
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new DateTime representing the end of the day (23:59:59).
-
1
def end_of_day
-
change(:hour => 23, :min => 59, :sec => 59)
-
end
-
-
# Returns a new DateTime representing the start of the hour (hh:00:00).
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new DateTime representing the end of the hour (hh:59:59).
-
1
def end_of_hour
-
change(:min => 59, :sec => 59)
-
end
-
-
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
-
#
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
-
1
def utc
-
new_offset(0)
-
end
-
1
alias_method :getutc, :utc
-
-
# Returns +true+ if <tt>offset == 0</tt>.
-
1
def utc?
-
offset == 0
-
end
-
-
# Returns the offset value in seconds.
-
1
def utc_offset
-
(offset * 86400).to_i
-
end
-
-
# Layers additional behavior on DateTime#<=> so that Time and
-
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
-
1
def <=>(other)
-
super other.to_datetime
-
end
-
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/values/time_zone'
-
-
1
class DateTime
-
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# === Examples
-
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
-
#
-
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:number) # => "20071204000000"
-
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
-
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
-
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
-
# datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
-
#
-
# == Adding your own datetime formats to to_formatted_s
-
# DateTime formats are shared with Time. You can add your own to the
-
# Time::DATE_FORMATS hash. Use the format name as the hash key and
-
# either a strftime string or Proc instance that takes a time or
-
# datetime argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
-
1
alias_method :to_s, :to_formatted_s
-
-
#
-
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
-
# datetime.formatted_offset # => "-06:00"
-
# datetime.formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
-
1
def readable_inspect
-
to_s(:rfc822)
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Returns DateTime with local offset for given year if format is local else
-
# offset is zero.
-
#
-
# DateTime.civil_from_format :local, 2012
-
# # => Sun, 01 Jan 2012 00:00:00 +0300
-
# DateTime.civil_from_format :local, 2012, 12, 17
-
# # => Mon, 17 Dec 2012 00:00:00 +0000
-
1
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
-
if utc_or_local.to_sym == :local
-
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
-
else
-
offset = 0
-
end
-
civil(year, month, day, hour, min, sec, offset)
-
end
-
-
# Converts +self+ to a floating-point number of seconds since the Unix epoch.
-
1
def to_f
-
seconds_since_unix_epoch.to_f
-
end
-
-
# Converts +self+ to an integer number of seconds since the Unix epoch.
-
1
def to_i
-
seconds_since_unix_epoch.to_i
-
end
-
-
1
private
-
-
1
def offset_in_seconds
-
(offset * 86400).to_i
-
end
-
-
1
def seconds_since_unix_epoch
-
(jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
-
end
-
end
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class DateTime
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
-
# as the local zone instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone
-
# as an argument, and the conversion will be based on that zone instead of
-
# <tt>Time.zone</tt>.
-
#
-
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
if zone
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
else
-
self
-
end
-
end
-
end
-
1
module Enumerable
-
# Calculates a sum from the elements.
-
#
-
# payments.sum { |p| p.price * p.tax_rate }
-
# payments.sum(&:price)
-
#
-
# The latter is a shortcut for:
-
#
-
# payments.inject(0) { |sum, p| sum + p.price }
-
#
-
# It can also calculate the sum without the use of a block.
-
#
-
# [5, 15, 10].sum # => 30
-
# ['foo', 'bar'].sum # => "foobar"
-
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
-
#
-
# The default sum of an empty list is zero. You can override this default:
-
#
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
-
1
def sum(identity = 0, &block)
-
if block_given?
-
map(&block).sum(identity)
-
else
-
inject { |sum, element| sum + element } || identity
-
end
-
end
-
-
# Convert an enumerable to a hash.
-
#
-
# people.index_by(&:login)
-
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
-
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
-
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
-
1
def index_by
-
if block_given?
-
Hash[map { |elem| [yield(elem), elem] }]
-
else
-
to_enum :index_by
-
end
-
end
-
-
# Returns +true+ if the enumerable has more than 1 element. Functionally
-
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
-
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
-
# if more than one person is over 26.
-
1
def many?
-
cnt = 0
-
if block_given?
-
any? do |element|
-
cnt += 1 if yield element
-
cnt > 1
-
end
-
else
-
any? { (cnt += 1) > 1 }
-
end
-
end
-
-
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
-
# collection does not include the object.
-
1
def exclude?(object)
-
!include?(object)
-
end
-
end
-
-
1
class Range #:nodoc:
-
# Optimize range sum to use arithmetic progression if a block is not given and
-
# we have a range of numeric values.
-
1
def sum(identity = 0)
-
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
-
super
-
else
-
actual_last = exclude_end? ? (last - 1) : last
-
if actual_last >= first
-
(actual_last - first + 1) * (actual_last + first) / 2
-
else
-
identity
-
end
-
end
-
end
-
end
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Hash
-
# Returns a string containing an XML representation of its receiver:
-
#
-
# {'foo' => 1, 'bar' => 2}.to_xml
-
# # =>
-
# # <?xml version="1.0" encoding="UTF-8"?>
-
# # <hash>
-
# # <foo type="integer">1</foo>
-
# # <bar type="integer">2</bar>
-
# # </hash>
-
#
-
# To do so, the method loops over the pairs and builds nodes that depend on
-
# the _values_. Given a pair +key+, +value+:
-
#
-
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
-
#
-
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
-
# and +key+ singularized as <tt>:children</tt>.
-
#
-
# * If +value+ is a callable object it must expect one or two arguments. Depending
-
# on the arity, the callable is invoked with the +options+ hash as first argument
-
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
-
# callable can add nodes by using <tt>options[:builder]</tt>.
-
#
-
# 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
-
# # => "<b>foo</b>"
-
#
-
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
-
#
-
# class Foo
-
# def to_xml(options)
-
# options[:builder].bar 'fooing!'
-
# end
-
# end
-
#
-
# { foo: Foo.new }.to_xml(skip_instruct: true)
-
# # => "<hash><bar>fooing!</bar></hash>"
-
#
-
# * Otherwise, a node with +key+ as tag is created with a string representation of
-
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
-
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
-
# added as well according to the following mapping:
-
#
-
# XML_TYPE_NAMES = {
-
# "Symbol" => "symbol",
-
# "Fixnum" => "integer",
-
# "Bignum" => "integer",
-
# "BigDecimal" => "decimal",
-
# "Float" => "float",
-
# "TrueClass" => "boolean",
-
# "FalseClass" => "boolean",
-
# "Date" => "date",
-
# "DateTime" => "dateTime",
-
# "Time" => "dateTime"
-
# }
-
#
-
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
-
#
-
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
-
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
-
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
-
1
def to_xml(options = {})
-
1
require 'active_support/builder' unless defined?(Builder)
-
-
1
options = options.dup
-
1
options[:indent] ||= 2
-
1
options[:root] ||= 'hash'
-
1
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
-
-
1
builder = options[:builder]
-
1
builder.instruct! unless options.delete(:skip_instruct)
-
-
1
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
-
1
builder.tag!(root) do
-
2
each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
-
1
yield builder if block_given?
-
end
-
end
-
-
1
class << self
-
1
def from_xml(xml)
-
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
-
end
-
-
1
private
-
1
def typecast_xml_value(value)
-
case value
-
when Hash
-
if value['type'] == 'array'
-
_, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
-
if entries.nil? || (c = value['__content__'] && c.blank?)
-
[]
-
else
-
case entries # something weird with classes not matching here. maybe singleton methods breaking is_a?
-
when Array
-
entries.collect { |v| typecast_xml_value(v) }
-
when Hash
-
[typecast_xml_value(entries)]
-
else
-
raise "can't typecast #{entries.inspect}"
-
end
-
end
-
elsif value['type'] == 'file' ||
-
(value['__content__'] && (value.keys.size == 1 || value['__content__'].present?))
-
content = value['__content__']
-
if parser = ActiveSupport::XmlMini::PARSING[value['type']]
-
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
-
else
-
content
-
end
-
elsif value['type'] == 'string' && value['nil'] != 'true'
-
''
-
# blank or nil parsed values are represented by nil
-
elsif value.blank? || value['nil'] == 'true'
-
nil
-
# If the type is the only element which makes it then
-
# this still makes the value nil, except if type is
-
# a XML node(where type['value'] is a Hash)
-
elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
-
nil
-
else
-
xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }]
-
-
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
-
# how multipart uploaded files from HTML appear
-
xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
-
end
-
when Array
-
value.map! { |i| typecast_xml_value(i) }
-
value.length > 1 ? value : value.first
-
when String
-
value
-
else
-
raise "can't typecast #{value.class.name} - #{value.inspect}"
-
end
-
end
-
-
1
def unrename_keys(params)
-
case params
-
when Hash
-
Hash[params.map { |k,v| [k.to_s.tr('-', '_'), unrename_keys(v)] } ]
-
when Array
-
params.map { |v| unrename_keys(v) }
-
else
-
params
-
end
-
end
-
end
-
end
-
1
class Hash
-
# Return a hash that includes everything but the given keys. This is useful for
-
# limiting a set of parameters to everything but a few known toggles:
-
#
-
# @person.update_attributes(params[:person].except(:admin))
-
1
def except(*keys)
-
3731
dup.except!(*keys)
-
end
-
-
# Replaces the hash without the given keys.
-
1
def except!(*keys)
-
36712
keys.each { |key| delete(key) }
-
3731
self
-
end
-
end
-
1
require 'active_support/hash_with_indifferent_access'
-
-
1
class Hash
-
-
# Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
-
#
-
# { a: 1 }.with_indifferent_access['a'] # => 1
-
1
def with_indifferent_access
-
ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
-
end
-
-
# Called when object is nested under an object that receives
-
# #with_indifferent_access. This method will be called on the current object
-
# by the enclosing object and is aliased to #with_indifferent_access by
-
# default. Subclasses of Hash may overwrite this method to return +self+ if
-
# converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
-
# desirable.
-
#
-
# b = { b: 1 }
-
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
-
1
alias nested_under_indifferent_access with_indifferent_access
-
end
-
1
class Hash
-
# Return a new hash with all keys converted using the block operation.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.transform_keys{ |key| key.to_s.upcase }
-
# # => { "NAME" => "Rob", "AGE" => "28" }
-
1
def transform_keys
-
272
result = {}
-
272
each_key do |key|
-
367
result[yield(key)] = self[key]
-
end
-
272
result
-
end
-
-
# Destructively convert all keys using the block operations.
-
# Same as transform_keys but modifies +self+.
-
1
def transform_keys!
-
keys.each do |key|
-
self[yield(key)] = delete(key)
-
end
-
self
-
end
-
-
# Return a new hash with all keys converted to strings.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.stringify_keys
-
# #=> { "name" => "Rob", "age" => "28" }
-
1
def stringify_keys
-
transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings. Same as
-
# +stringify_keys+, but modifies +self+.
-
1
def stringify_keys!
-
transform_keys!{ |key| key.to_s }
-
end
-
-
# Return a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+.
-
#
-
# hash = { 'name' => 'Rob', 'age' => '28' }
-
#
-
# hash.symbolize_keys
-
# #=> { name: "Rob", age: "28" }
-
1
def symbolize_keys
-
639
transform_keys{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options, :symbolize_keys
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
-
1
def symbolize_keys!
-
transform_keys!{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options!, :symbolize_keys!
-
-
# Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
-
# on a mismatch. Note that keys are NOT treated indifferently, meaning if you
-
# use strings for keys but assert symbols as keys, this will fail.
-
#
-
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
-
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
-
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
-
1
def assert_valid_keys(*valid_keys)
-
valid_keys.flatten!
-
each_key do |k|
-
raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
-
end
-
end
-
-
# Return a new hash with all keys converted by the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_transform_keys{ |key| key.to_s.upcase }
-
# # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
-
1
def deep_transform_keys(&block)
-
1208
result = {}
-
1208
each do |key, value|
-
2502
result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
-
end
-
1208
result
-
end
-
-
# Destructively convert all keys by using the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_transform_keys!(&block)
-
keys.each do |key|
-
value = delete(key)
-
self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
-
end
-
self
-
end
-
-
# Return a new hash with all keys converted to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_stringify_keys
-
# # => { "person" => { "name" => "Rob", "age" => "28" } }
-
1
def deep_stringify_keys
-
deep_transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_stringify_keys!
-
deep_transform_keys!{ |key| key.to_s }
-
end
-
-
# Return a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+. This includes the keys from the root hash
-
# and from all nested hashes.
-
#
-
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
-
#
-
# hash.deep_symbolize_keys
-
# # => { person: { name: "Rob", age: "28" } }
-
1
def deep_symbolize_keys
-
2745
deep_transform_keys{ |key| key.to_sym rescue key }
-
end
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_symbolize_keys!
-
deep_transform_keys!{ |key| key.to_sym rescue key }
-
end
-
end
-
1
class Hash
-
# Merges the caller into +other_hash+. For example,
-
#
-
# options = options.reverse_merge(size: 25, velocity: 10)
-
#
-
# is equivalent to
-
#
-
# options = { size: 25, velocity: 10 }.merge(options)
-
#
-
# This is particularly useful for initializing an options hash
-
# with default values.
-
1
def reverse_merge(other_hash)
-
other_hash.merge(self)
-
end
-
-
# Destructive +reverse_merge+.
-
1
def reverse_merge!(other_hash)
-
# right wins if there is no left
-
merge!( other_hash ){|key,left,right| left }
-
end
-
1
alias_method :reverse_update, :reverse_merge!
-
end
-
1
class Hash
-
# Slice a hash to include only the given keys. This is useful for
-
# limiting an options hash to valid keys before passing to a method:
-
#
-
# def search(criteria = {})
-
# criteria.assert_valid_keys(:mass, :velocity, :time)
-
# end
-
#
-
# search(options.slice(:mass, :velocity, :time))
-
#
-
# If you have an array of keys you want to limit to, you should splat them:
-
#
-
# valid_keys = [:mass, :velocity, :time]
-
# search(options.slice(*valid_keys))
-
1
def slice(*keys)
-
275
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
2020
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
-
end
-
-
# Replaces the hash with only the given keys.
-
# Returns a hash containing the removed key/value pairs.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
-
# # => {:c=>3, :d=>4}
-
1
def slice!(*keys)
-
31
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
31
omit = slice(*self.keys - keys)
-
31
hash = slice(*keys)
-
31
replace(hash)
-
31
omit
-
end
-
-
# Removes and returns the key/value pairs matching the given keys.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
-
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
-
1
def extract!(*keys)
-
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
-
end
-
end
-
1
class Integer
-
# Enables the use of time calculations and declarations, like <tt>45.minutes +
-
# 2.hours + 4.years</tt>.
-
#
-
# These methods use Time#advance for precise date calculations when using
-
# <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
-
# results from a Time object.
-
#
-
# # equivalent to Time.now.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.now.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.now.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples
-
# above, care should be taken to note that this is not true if the result of
-
# +months+, +years+, etc is converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def months
-
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
-
end
-
1
alias :month :months
-
-
1
def years
-
ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
-
end
-
1
alias :year :years
-
end
-
1
module Kernel
-
# class_eval on an object acts like singleton_class.class_eval.
-
1
def class_eval(*args, &block)
-
singleton_class.class_eval(*args, &block)
-
end
-
end
-
1
class Module
-
# Encapsulates the common pattern of:
-
#
-
# alias_method :foo_without_feature, :foo
-
# alias_method :foo, :foo_with_feature
-
#
-
# With this, you simply do:
-
#
-
# alias_method_chain :foo, :feature
-
#
-
# And both aliases are set up for you.
-
#
-
# Query and bang methods (foo?, foo!) keep the same punctuation:
-
#
-
# alias_method_chain :foo?, :feature
-
#
-
# is equivalent to
-
#
-
# alias_method :foo_without_feature?, :foo?
-
# alias_method :foo?, :foo_with_feature?
-
#
-
# so you can safely chain foo, foo?, and foo! with the same feature.
-
1
def alias_method_chain(target, feature)
-
# Strip out punctuation on predicates or bang methods since
-
# e.g. target?_without_feature is not a valid method name.
-
1
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
-
1
yield(aliased_target, punctuation) if block_given?
-
-
1
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
-
1
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
-
-
1
alias_method without_method, target
-
1
alias_method target, with_method
-
-
case
-
when public_method_defined?(without_method)
-
1
public target
-
when protected_method_defined?(without_method)
-
protected target
-
when private_method_defined?(without_method)
-
private target
-
1
end
-
end
-
-
# Allows you to make aliases for attributes, which includes
-
# getter, setter, and query methods.
-
#
-
# class Content < ActiveRecord::Base
-
# # has a title attribute
-
# end
-
#
-
# class Email < Content
-
# alias_attribute :subject, :title
-
# end
-
#
-
# e = Email.find(1)
-
# e.title # => "Superstars"
-
# e.subject # => "Superstars"
-
# e.subject? # => true
-
# e.subject = "Megastars"
-
# e.title # => "Megastars"
-
1
def alias_attribute(new_name, old_name)
-
module_eval <<-STR, __FILE__, __LINE__ + 1
-
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
-
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
-
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
-
STR
-
end
-
end
-
1
class Module
-
# A module may or may not have a name.
-
#
-
# module M; end
-
# M.name # => "M"
-
#
-
# m = Module.new
-
# m.name # => nil
-
#
-
# A module gets a name when it is first assigned to a constant. Either
-
# via the +module+ or +class+ keyword or by an explicit assignment:
-
#
-
# m = Module.new # creates an anonymous module
-
# M = m # => m gets a name here as a side-effect
-
# m.name # => "M"
-
1
def anonymous?
-
5
name.nil?
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Module
-
1
def mattr_reader(*syms)
-
1
options = syms.extract_options!
-
1
syms.each do |sym|
-
1
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
1
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
@@#{sym} = nil unless defined? @@#{sym}
-
-
def self.#{sym}
-
@@#{sym}
-
end
-
EOS
-
-
1
unless options[:instance_reader] == false || options[:instance_accessor] == false
-
1
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}
-
@@#{sym}
-
end
-
EOS
-
end
-
end
-
end
-
-
1
def mattr_writer(*syms)
-
1
options = syms.extract_options!
-
1
syms.each do |sym|
-
1
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
1
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
-
1
unless options[:instance_writer] == false || options[:instance_accessor] == false
-
1
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
end
-
end
-
end
-
-
# Extends the module object with module and instance accessors for class attributes,
-
# just like the native attr* accessors for instance attributes.
-
#
-
# module AppConfiguration
-
# mattr_accessor :google_api_key
-
#
-
# self.google_api_key = "123456789"
-
# end
-
#
-
# AppConfiguration.google_api_key # => "123456789"
-
# AppConfiguration.google_api_key = "overriding the api key!"
-
# AppConfiguration.google_api_key # => "overriding the api key!"
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
-
1
def mattr_accessor(*syms)
-
1
mattr_reader(*syms)
-
1
mattr_writer(*syms)
-
end
-
end
-
1
class Module
-
# Provides a delegate class method to easily expose contained objects' public methods
-
# as your own. Pass one or more methods (specified as symbols or strings)
-
# and the name of the target object via the <tt>:to</tt> option (also a symbol
-
# or string). At least one method and the <tt>:to</tt> option are required.
-
#
-
# Delegation is particularly useful with Active Record associations:
-
#
-
# class Greeter < ActiveRecord::Base
-
# def hello
-
# 'hello'
-
# end
-
#
-
# def goodbye
-
# 'goodbye'
-
# end
-
# end
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, to: :greeter
-
# end
-
#
-
# Foo.new.hello # => "hello"
-
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
-
#
-
# Multiple delegates to the same target are allowed:
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, :goodbye, to: :greeter
-
# end
-
#
-
# Foo.new.goodbye # => "goodbye"
-
#
-
# Methods can be delegated to instance variables, class variables, or constants
-
# by providing them as a symbols:
-
#
-
# class Foo
-
# CONSTANT_ARRAY = [0,1,2,3]
-
# @@class_array = [4,5,6,7]
-
#
-
# def initialize
-
# @instance_array = [8,9,10,11]
-
# end
-
# delegate :sum, to: :CONSTANT_ARRAY
-
# delegate :min, to: :@@class_array
-
# delegate :max, to: :@instance_array
-
# end
-
#
-
# Foo.new.sum # => 6
-
# Foo.new.min # => 4
-
# Foo.new.max # => 11
-
#
-
# It's also possible to delegate a method to the class by using +:class+:
-
#
-
# class Foo
-
# def self.hello
-
# "world"
-
# end
-
#
-
# delegate :hello, to: :class
-
# end
-
#
-
# Foo.new.hello # => "world"
-
#
-
# Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
-
# is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
-
# delegated to.
-
#
-
# Person = Struct.new(:name, :address)
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: true
-
# end
-
#
-
# john_doe = Person.new('John Doe', 'Vimmersvej 13')
-
# invoice = Invoice.new(john_doe)
-
# invoice.client_name # => "John Doe"
-
# invoice.client_address # => "Vimmersvej 13"
-
#
-
# It is also possible to supply a custom prefix.
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: :customer
-
# end
-
#
-
# invoice = Invoice.new(john_doe)
-
# invoice.customer_name # => 'John Doe'
-
# invoice.customer_address # => 'Vimmersvej 13'
-
#
-
# If the delegate object is +nil+ an exception is raised, and that happens
-
# no matter whether +nil+ responds to the delegated method. You can get a
-
# +nil+ instead with the +:allow_nil+ option.
-
#
-
# class Foo
-
# attr_accessor :bar
-
# def initialize(bar = nil)
-
# @bar = bar
-
# end
-
# delegate :zoo, to: :bar
-
# end
-
#
-
# Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
-
#
-
# class Foo
-
# attr_accessor :bar
-
# def initialize(bar = nil)
-
# @bar = bar
-
# end
-
# delegate :zoo, to: :bar, allow_nil: true
-
# end
-
#
-
# Foo.new.zoo # returns nil
-
1
def delegate(*methods)
-
18
options = methods.pop
-
18
unless options.is_a?(Hash) && to = options[:to]
-
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
-
end
-
-
18
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
-
-
18
if prefix == true && to =~ /^[^a-z_]/
-
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
-
end
-
-
18
method_prefix = \
-
if prefix
-
"#{prefix == true ? to : prefix}_"
-
else
-
18
''
-
end
-
-
18
file, line = caller.first.split(':', 2)
-
18
line = line.to_i
-
-
18
to = to.to_s
-
18
to = 'self.class' if to == 'class'
-
-
18
methods.each do |method|
-
# Attribute writer methods only accept one argument. Makes sure []=
-
# methods still accept two arguments.
-
30
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
-
-
30
if allow_nil
-
module_eval(<<-EOS, file, line - 2)
-
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
-
if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
-
#{to}.#{method}(#{definition}) # client.name(*args, &block)
-
end # end
-
end # end
-
EOS
-
else
-
30
exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
-
-
30
module_eval(<<-EOS, file, line - 1)
-
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
-
#{to}.#{method}(#{definition}) # client.name(*args, &block)
-
rescue NoMethodError # rescue NoMethodError
-
if #{to}.nil? # if client.nil?
-
#{exception} # # add helpful message to the exception
-
else # else
-
raise # raise
-
end # end
-
end # end
-
EOS
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation/method_wrappers'
-
-
1
class Module
-
# deprecate :foo
-
# deprecate bar: 'message'
-
# deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
-
#
-
# You can also use custom deprecator instance:
-
#
-
# deprecate :foo, deprecator: MyLib::Deprecator.new
-
# deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
-
#
-
# \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
-
# method where you can implement your custom warning behavior.
-
#
-
# class MyLib::Deprecator
-
# def deprecation_warning(deprecated_method_name, message, caller_backtrace)
-
# message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
-
# Kernel.warn message
-
# end
-
# end
-
1
def deprecate(*method_names)
-
ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
-
end
-
end
-
1
require 'active_support/inflector'
-
-
1
class Module
-
# Returns the name of the module containing this one.
-
#
-
# M::N.parent_name # => "M"
-
1
def parent_name
-
27
if defined? @parent_name
-
7
@parent_name
-
else
-
20
@parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
-
end
-
end
-
-
# Returns the module which contains this one according to its name.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M::N.parent # => M
-
# X.parent # => M
-
#
-
# The parent of top-level and anonymous modules is Object.
-
#
-
# M.parent # => Object
-
# Module.new.parent # => Object
-
1
def parent
-
parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
-
end
-
-
# Returns all the parents of this module according to its name, ordered from
-
# nested outwards. The receiver is not contained within the result.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M.parents # => [Object]
-
# M::N.parents # => [M, Object]
-
# X.parents # => [M, Object]
-
1
def parents
-
20
parents = []
-
20
if parent_name
-
7
parts = parent_name.split('::')
-
7
until parts.empty?
-
7
parents << ActiveSupport::Inflector.constantize(parts * '::')
-
7
parts.pop
-
end
-
end
-
20
parents << Object unless parents.include? Object
-
20
parents
-
end
-
-
1
def local_constants #:nodoc:
-
constants(false)
-
end
-
-
# *DEPRECATED*: Use +local_constants+ instead.
-
#
-
# Returns the names of the constants defined locally as strings.
-
#
-
# module M
-
# X = 1
-
# end
-
# M.local_constant_names # => ["X"]
-
#
-
# This method is useful for forward compatibility, since Ruby 1.8 returns
-
# constant names as strings, whereas 1.9 returns them as symbols.
-
1
def local_constant_names
-
ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
-
local_constants.map { |c| c.to_s }
-
end
-
end
-
1
class Module
-
1
def remove_possible_method(method)
-
1948
if method_defined?(method) || private_method_defined?(method)
-
1896
undef_method(method)
-
end
-
end
-
-
1
def redefine_method(method, &block)
-
8
remove_possible_method(method)
-
8
define_method(method, &block)
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/acts_like'
-
-
1
class Numeric
-
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
-
#
-
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
-
# as well as adding or subtracting their results from a Time object. For example:
-
#
-
# # equivalent to Time.current.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.current.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.current.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples above, care
-
# should be taken to note that this is not true if the result of `months', `years', etc is
-
# converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def seconds
-
ActiveSupport::Duration.new(self, [[:seconds, self]])
-
end
-
1
alias :second :seconds
-
-
1
def minutes
-
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
-
end
-
1
alias :minute :minutes
-
-
1
def hours
-
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
-
end
-
1
alias :hour :hours
-
-
1
def days
-
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
-
end
-
1
alias :day :days
-
-
1
def weeks
-
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
-
end
-
1
alias :week :weeks
-
-
1
def fortnights
-
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
-
end
-
1
alias :fortnight :fortnights
-
-
# Reads best without arguments: 10.minutes.ago
-
1
def ago(time = ::Time.current)
-
time - self
-
end
-
-
# Reads best with argument: 10.minutes.until(time)
-
1
alias :until :ago
-
-
# Reads best with argument: 10.minutes.since(time)
-
1
def since(time = ::Time.current)
-
time + self
-
end
-
-
# Reads best without arguments: 10.minutes.from_now
-
1
alias :from_now :since
-
end
-
1
class Object
-
# A duck-type assistant method. For example, Active Support extends Date
-
# to define an acts_like_date? method, and extends Time to define
-
# acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
-
# "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
-
# we want to act like Time simply need to define an acts_like_time? method.
-
1
def acts_like?(duck)
-
respond_to? :"acts_like_#{duck}?"
-
end
-
end
-
# encoding: utf-8
-
-
1
class Object
-
# An object is blank if it's false, empty, or a whitespace string.
-
# For example, '', ' ', +nil+, [], and {} are all blank.
-
#
-
# This simplifies:
-
#
-
# if address.nil? || address.empty?
-
#
-
# ...to:
-
#
-
# if address.blank?
-
1
def blank?
-
respond_to?(:empty?) ? empty? : !self
-
end
-
-
# An object is present if it's not <tt>blank?</tt>.
-
1
def present?
-
6
!blank?
-
end
-
-
# Returns object if it's <tt>present?</tt> otherwise returns +nil+.
-
# <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
-
#
-
# This is handy for any representation of objects where blank is the same
-
# as not present at all. For example, this simplifies a common check for
-
# HTTP POST/query parameters:
-
#
-
# state = params[:state] if params[:state].present?
-
# country = params[:country] if params[:country].present?
-
# region = state || country || 'US'
-
#
-
# ...becomes:
-
#
-
# region = params[:state].presence || params[:country].presence || 'US'
-
1
def presence
-
self if present?
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is blank:
-
#
-
# nil.blank? # => true
-
1
def blank?
-
129
true
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is blank:
-
#
-
# false.blank? # => true
-
1
def blank?
-
true
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not blank:
-
#
-
# true.blank? # => false
-
1
def blank?
-
false
-
end
-
end
-
-
1
class Array
-
# An array is blank if it's empty:
-
#
-
# [].blank? # => true
-
# [1,2,3].blank? # => false
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class Hash
-
# A hash is blank if it's empty:
-
#
-
# {}.blank? # => true
-
# { key: 'value' }.blank? # => false
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class String
-
# A string is blank if it's empty or contains whitespaces only:
-
#
-
# ''.blank? # => true
-
# ' '.blank? # => true
-
# ' '.blank? # => true
-
# ' something here '.blank? # => false
-
1
def blank?
-
432
self !~ /[^[:space:]]/
-
end
-
end
-
-
1
class Numeric #:nodoc:
-
# No number is blank:
-
#
-
# 1.blank? # => false
-
# 0.blank? # => false
-
1
def blank?
-
101
false
-
end
-
end
-
#--
-
# Most objects are cloneable, but not all. For example you can't dup +nil+:
-
#
-
# nil.dup # => TypeError: can't dup NilClass
-
#
-
# Classes may signal their instances are not duplicable removing +dup+/+clone+
-
# or raising exceptions from them. So, to dup an arbitrary object you normally
-
# use an optimistic approach and are ready to catch an exception, say:
-
#
-
# arbitrary_object.dup rescue object
-
#
-
# Rails dups objects in a few critical spots where they are not that arbitrary.
-
# That rescue is very expensive (like 40 times slower than a predicate), and it
-
# is often triggered.
-
#
-
# That's why we hardcode the following cases and check duplicable? instead of
-
# using that rescue idiom.
-
#++
-
1
class Object
-
# Can you safely dup this object?
-
#
-
# False for +nil+, +false+, +true+, symbol, and number objects;
-
# true otherwise.
-
1
def duplicable?
-
2
true
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is not duplicable:
-
#
-
# nil.duplicable? # => false
-
# nil.dup # => TypeError: can't dup NilClass
-
1
def duplicable?
-
9
false
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is not duplicable:
-
#
-
# false.duplicable? # => false
-
# false.dup # => TypeError: can't dup FalseClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not duplicable:
-
#
-
# true.duplicable? # => false
-
# true.dup # => TypeError: can't dup TrueClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Symbol
-
# Symbols are not duplicable:
-
#
-
# :my_symbol.duplicable? # => false
-
# :my_symbol.dup # => TypeError: can't dup Symbol
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Numeric
-
# Numbers are not duplicable:
-
#
-
# 3.duplicable? # => false
-
# 3.dup # => TypeError: can't dup Fixnum
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
require 'bigdecimal'
-
1
class BigDecimal
-
1
begin
-
1
BigDecimal.new('4.56').dup
-
-
def duplicable?
-
true
-
end
-
rescue TypeError
-
# can't dup, so use superclass implementation
-
end
-
end
-
1
class Object
-
# Returns a hash with string keys that maps instance variable names without "@" to their
-
# corresponding values.
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
-
1
def instance_values
-
645
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
-
end
-
-
# Returns an array of instance variable names including "@".
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-
1
def instance_variable_names
-
instance_variables.map { |var| var.to_s }
-
end
-
end
-
# Hack to load json gem first so we can overwrite its to_json.
-
1
begin
-
1
require 'json'
-
rescue LoadError
-
end
-
-
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
-
# their default behavior. That said, we need to define the basic to_json method in all of them,
-
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
-
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
-
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
-
1
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
-
9
klass.class_eval do
-
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
-
9
def to_json(options = nil)
-
19
ActiveSupport::JSON.encode(self, options)
-
end
-
end
-
end
-
-
1
module Process
-
1
class Status
-
1
def as_json(options = nil)
-
{ :exitstatus => exitstatus, :pid => pid }
-
end
-
end
-
end
-
1
class Object
-
# Alias of <tt>to_s</tt>.
-
1
def to_param
-
to_s
-
end
-
end
-
-
1
class NilClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class TrueClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class FalseClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class Array
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
-
1
def to_param
-
collect { |e| e.to_param }.join '/'
-
end
-
end
-
-
1
class Hash
-
# Returns a string representation of the receiver suitable for use as a URL
-
# query string:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_param
-
# # => "name=David&nationality=Danish"
-
#
-
# An optional namespace can be passed to enclose the param names:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_param('user')
-
# # => "user[name]=David&user[nationality]=Danish"
-
#
-
# The string pairs "key=value" that conform the query string
-
# are sorted lexicographically in ascending order.
-
#
-
# This method is also aliased as +to_query+.
-
1
def to_param(namespace = nil)
-
collect do |key, value|
-
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
-
end.sort * '&'
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
-
1
class Object
-
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
-
# param name.
-
#
-
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
-
1
def to_query(key)
-
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
-
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
-
end
-
end
-
-
1
class Array
-
# Converts an array into a string suitable for use as a URL query string,
-
# using the given +key+ as the param name.
-
#
-
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
-
1
def to_query(key)
-
prefix = "#{key}[]"
-
collect { |value| value.to_query(prefix) }.join '&'
-
end
-
end
-
-
1
class Hash
-
1
alias_method :to_query, :to_param
-
end
-
1
class Object
-
# Invokes the public method identified by the symbol +method+, passing it any arguments
-
# and/or the block specified, just like the regular Ruby <tt>Object#public_send</tt> does.
-
#
-
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
-
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
-
#
-
# This is also true if the receiving object does not implemented the tried method. It will
-
# return +nil+ in that case as well.
-
#
-
# If try is called without a method to call, it will yield any given block with the object.
-
#
-
# Please also note that +try+ is defined on +Object+, therefore it won't work with
-
# subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
-
# delegate +try+ to target instead of calling it on delegator itself.
-
#
-
# Without +try+
-
# @person && @person.name
-
# or
-
# @person ? @person.name : nil
-
#
-
# With +try+
-
# @person.try(:name)
-
#
-
# +try+ also accepts arguments and/or a block, for the method it is trying
-
# Person.try(:find, 1)
-
# @people.try(:collect) {|p| p.name}
-
#
-
# Without a method argument try will yield to the block unless the receiver is nil.
-
# @person.try { |p| "#{p.first_name} #{p.last_name}" }
-
#
-
# +try+ behaves like +Object#public_send+, unless called on +NilClass+.
-
1
def try(*a, &b)
-
7
if a.empty? && block_given?
-
yield self
-
else
-
7
public_send(*a, &b) if respond_to?(a.first)
-
end
-
end
-
-
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
-
# does not implemented the tried method.
-
1
def try!(*a, &b)
-
if a.empty? && block_given?
-
yield self
-
else
-
public_send(*a, &b)
-
end
-
end
-
end
-
-
1
class NilClass
-
# Calling +try+ on +nil+ always returns +nil+.
-
# It becomes specially helpful when navigating through associations that may return +nil+.
-
#
-
# nil.try(:name) # => nil
-
#
-
# Without +try+
-
# @person && !@person.children.blank? && @person.children.first.name
-
#
-
# With +try+
-
# @person.try(:children).try(:first).try(:name)
-
1
def try(*args)
-
nil
-
end
-
-
1
def try!(*args)
-
nil
-
end
-
end
-
1
require 'active_support/core_ext/range/conversions'
-
1
require 'active_support/core_ext/range/include_range'
-
1
require 'active_support/core_ext/range/overlaps'
-
1
class Range
-
1
RANGE_FORMATS = {
-
:db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
-
}
-
-
# Gives a human readable format of the range.
-
#
-
# (1..100).to_formatted_s # => "1..100"
-
1
def to_formatted_s(format = :default)
-
if formatter = RANGE_FORMATS[format]
-
formatter.call(first, last)
-
else
-
to_default_s
-
end
-
end
-
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
class Range
-
# Extends the default Range#include? to support range comparisons.
-
# (1..5).include?(1..5) # => true
-
# (1..5).include?(2..3) # => true
-
# (1..5).include?(2..6) # => false
-
#
-
# The native Range#include? behavior is untouched.
-
# ('a'..'f').include?('c') # => true
-
# (5..9).include?(11) # => false
-
1
def include_with_range?(value)
-
if value.is_a?(::Range)
-
# 1...10 includes 1..9 but it does not include 1..10.
-
operator = exclude_end? && !value.exclude_end? ? :< : :<=
-
include_without_range?(value.first) && value.last.send(operator, last)
-
else
-
include_without_range?(value)
-
end
-
end
-
-
1
alias_method_chain :include?, :range
-
end
-
1
class Range
-
# Compare two ranges and see if they overlap each other
-
# (1..5).overlaps?(4..6) # => true
-
# (1..5).overlaps?(7..9) # => false
-
1
def overlaps?(other)
-
cover?(other.first) || other.cover?(first)
-
end
-
end
-
1
class String
-
# If you pass a single Fixnum, returns a substring of one character at that
-
# position. The first character of the string is at position 0, the next at
-
# position 1, and so on. If a range is supplied, a substring containing
-
# characters at offsets given by the range is returned. In both cases, if an
-
# offset is negative, it is counted from the end of the string. Returns nil
-
# if the initial offset falls outside the string. Returns an empty string if
-
# the beginning of the range is greater than the end of the string.
-
#
-
# str = "hello"
-
# str.at(0) #=> "h"
-
# str.at(1..3) #=> "ell"
-
# str.at(-2) #=> "l"
-
# str.at(-2..-1) #=> "lo"
-
# str.at(5) #=> nil
-
# str.at(5..-1) #=> ""
-
#
-
# If a Regexp is given, the matching portion of the string is returned.
-
# If a String is given, that given string is returned if it occurs in
-
# the string. In both cases, nil is returned if there is no match.
-
#
-
# str = "hello"
-
# str.at(/lo/) #=> "lo"
-
# str.at(/ol/) #=> nil
-
# str.at("lo") #=> "lo"
-
# str.at("ol") #=> nil
-
1
def at(position)
-
self[position]
-
end
-
-
# Returns a substring from the given position to the end of the string.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.from(0) #=> "hello"
-
# str.from(3) #=> "lo"
-
# str.from(-2) #=> "lo"
-
#
-
# You can mix it with +to+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) #=> "hello"
-
# str.from(1).to(-2) #=> "ell"
-
1
def from(position)
-
self[position..-1]
-
end
-
-
# Returns a substring from the beginning of the string to the given position.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.to(0) #=> "h"
-
# str.to(3) #=> "hell"
-
# str.to(-2) #=> "hell"
-
#
-
# You can mix it with +from+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) #=> "hello"
-
# str.from(1).to(-2) #=> "ell"
-
1
def to(position)
-
self[0..position]
-
end
-
-
# Returns the first character. If a limit is supplied, returns a substring
-
# from the beginning of the string until it reaches the limit value. If the
-
# given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.first #=> "h"
-
# str.first(1) #=> "h"
-
# str.first(2) #=> "he"
-
# str.first(0) #=> ""
-
# str.first(6) #=> "hello"
-
1
def first(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
to(limit - 1)
-
end
-
end
-
-
# Returns the last character of the string. If a limit is supplied, returns a substring
-
# from the end of the string until it reaches the limit value (counting backwards). If
-
# the given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.last #=> "o"
-
# str.last(1) #=> "o"
-
# str.last(2) #=> "lo"
-
# str.last(0) #=> ""
-
# str.last(6) #=> "hello"
-
1
def last(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
from(-limit)
-
end
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/inflector/transliterate'
-
-
# String inflections define new methods on the String class to transform names for different purposes.
-
# For instance, you can figure out the name of a table from the name of a class.
-
#
-
# 'ScaleScore'.tableize # => "scale_scores"
-
#
-
1
class String
-
# Returns the plural form of the word in the string.
-
#
-
# If the optional parameter +count+ is specified,
-
# the singular form will be returned if <tt>count == 1</tt>.
-
# For any other value of +count+ the plural will be returned.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be pluralized as a word of that language.
-
# By default, this parameter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'the blue mailman'.pluralize # => "the blue mailmen"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'apple'.pluralize(1) # => "apple"
-
# 'apple'.pluralize(2) # => "apples"
-
# 'ley'.pluralize(:es) # => "leyes"
-
# 'ley'.pluralize(1, :es) # => "ley"
-
1
def pluralize(count = nil, locale = :en)
-
locale = count if count.is_a?(Symbol)
-
if count == 1
-
self
-
else
-
ActiveSupport::Inflector.pluralize(self, locale)
-
end
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be singularized as a word of that language.
-
# By default, this paramter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'the blue mailmen'.singularize # => "the blue mailman"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(locale = :en)
-
6
ActiveSupport::Inflector.singularize(self, locale)
-
end
-
-
# +constantize+ tries to find a declared constant with the name specified
-
# in the string. It raises a NameError when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.constantize
-
#
-
# 'Module'.constantize # => Module
-
# 'Class'.constantize # => Class
-
# 'blargle'.constantize # => NameError: wrong constant name blargle
-
1
def constantize
-
44
ActiveSupport::Inflector.constantize(self)
-
end
-
-
# +safe_constantize+ tries to find a declared constant with the name specified
-
# in the string. It returns nil when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Class'.safe_constantize # => Class
-
# 'blargle'.safe_constantize # => nil
-
1
def safe_constantize
-
ActiveSupport::Inflector.safe_constantize(self)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
-
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
-
#
-
# 'active_record'.camelize # => "ActiveRecord"
-
# 'active_record'.camelize(:lower) # => "activeRecord"
-
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
-
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
-
1
def camelize(first_letter = :upper)
-
126
case first_letter
-
when :upper
-
115
ActiveSupport::Inflector.camelize(self, true)
-
when :lower
-
11
ActiveSupport::Inflector.camelize(self, false)
-
end
-
end
-
1
alias_method :camelcase, :camelize
-
-
# Capitalizes all the words and replaces some characters in the string to create
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
-
# used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
1
def titleize
-
ActiveSupport::Inflector.titleize(self)
-
end
-
1
alias_method :titlecase, :titleize
-
-
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
-
#
-
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
1
def underscore
-
59
ActiveSupport::Inflector.underscore(self)
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize
-
ActiveSupport::Inflector.dasherize(self)
-
end
-
-
# Removes the module part from the constant expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize
-
ActiveSupport::Inflector.demodulize(self)
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize
-
ActiveSupport::Inflector.deconstantize(self)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(sep = '-')
-
ActiveSupport::Inflector.parameterize(self, sep)
-
end
-
-
# Creates the name of a table like Rails does for models to table names. This method
-
# uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize
-
ActiveSupport::Inflector.tableize(self)
-
end
-
-
# Create a class name from a plural table name like Rails does for table names to models.
-
# Note that this returns a string and not a class. (To convert to an actual class
-
# follow +classify+ with +constantize+.)
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
#
-
# Singular names are not handled correctly.
-
#
-
# 'business'.classify # => "Busines"
-
1
def classify
-
ActiveSupport::Inflector.classify(self)
-
end
-
-
# Capitalizes the first word, turns underscores into spaces, and strips '_id'.
-
# Like +titleize+, this is meant for creating pretty output.
-
#
-
# 'employee_salary' # => "Employee salary"
-
# 'author_id' # => "Author"
-
1
def humanize
-
409
ActiveSupport::Inflector.humanize(self)
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(separate_class_name_and_id_with_underscore = true)
-
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/multibyte'
-
-
1
class String
-
# == Multibyte proxy
-
#
-
# +mb_chars+ is a multibyte safe proxy for string methods.
-
#
-
# In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
-
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
-
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
-
#
-
# name = 'Claus Müller'
-
# name.reverse # => "rell??M sualC"
-
# name.length # => 13
-
#
-
# name.mb_chars.reverse.to_s # => "rellüM sualC"
-
# name.mb_chars.length # => 12
-
#
-
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
-
# it becomes easy to run one version of your code on multiple Ruby versions.
-
#
-
# == Method chaining
-
#
-
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
-
# method chaining on the result of any of these methods.
-
#
-
# name.mb_chars.reverse.length # => 12
-
#
-
# == Interoperability and configuration
-
#
-
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
-
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
-
# object. Interoperability problems can be resolved easily with a +to_s+ call.
-
#
-
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
-
# information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
-
1
def mb_chars
-
if ActiveSupport::Multibyte.proxy_class.consumes?(self)
-
ActiveSupport::Multibyte.proxy_class.new(self)
-
else
-
self
-
end
-
end
-
-
1
def is_utf8?
-
case encoding
-
when Encoding::UTF_8
-
valid_encoding?
-
when Encoding::ASCII_8BIT, Encoding::US_ASCII
-
dup.force_encoding(Encoding::UTF_8).valid_encoding?
-
else
-
false
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Time
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/time_with_zone'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Time
-
1
include DateAndTime::Calculations
-
-
1
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-
-
1
class << self
-
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
-
1
def ===(other)
-
super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
-
end
-
-
# Return the number of days in the given month.
-
# If no year is specified, it will use the current year.
-
1
def days_in_month(month, year = now.year)
-
if month == 2 && ::Date.gregorian_leap?(year)
-
29
-
else
-
COMMON_YEAR_DAYS_IN_MONTH[month]
-
end
-
end
-
-
# Returns a new Time if requested year can be accommodated by Ruby's Time class
-
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
-
# otherwise returns a DateTime.
-
1
def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
-
time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
-
-
# This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
-
if time.year == year
-
time
-
else
-
::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
end
-
rescue
-
::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
-
1
def utc_time(*args)
-
time_with_datetime_fallback(:utc, *args)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
-
1
def local_time(*args)
-
time_with_datetime_fallback(:local, *args)
-
end
-
-
# Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now : ::Time.now
-
end
-
end
-
-
# Seconds since midnight: Time.now.seconds_since_midnight
-
1
def seconds_since_midnight
-
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
-
end
-
-
# Returns a new Time where one or more of the elements have been changed according
-
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
-
# then minute, sec, and usec is set to 0. If the hour and minute is passed, then
-
# sec and usec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>.
-
#
-
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
new_year = options.fetch(:year, year)
-
new_month = options.fetch(:month, month)
-
new_day = options.fetch(:day, day)
-
new_hour = options.fetch(:hour, hour)
-
new_min = options.fetch(:min, options[:hour] ? 0 : min)
-
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
-
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
-
-
if utc?
-
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
elsif zone
-
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
else
-
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
-
end
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
unless options[:weeks].nil?
-
options[:weeks], partial_weeks = options[:weeks].divmod(1)
-
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
-
end
-
-
unless options[:days].nil?
-
options[:days], partial_days = options[:days].divmod(1)
-
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
-
end
-
-
d = to_date.advance(options)
-
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
if seconds_to_advance.zero?
-
time_advanced_by_date
-
else
-
time_advanced_by_date.since(seconds_to_advance)
-
end
-
end
-
-
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new Time representing the time a number of seconds since the instance time
-
1
def since(seconds)
-
self + seconds
-
rescue
-
to_datetime.since(seconds)
-
end
-
1
alias :in :since
-
-
# Returns a new Time representing the start of the day (0:00)
-
1
def beginning_of_day
-
#(self - seconds_since_midnight).change(usec: 0)
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_day
-
change(
-
:hour => 23,
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
-
# Returns a new Time representing the start of the hour (x:00)
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_hour
-
change(
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
-
# Returns a Range representing the whole day of the current time.
-
1
def all_day
-
beginning_of_day..end_of_day
-
end
-
-
# Returns a Range representing the whole week of the current time.
-
# Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
-
1
def all_week(start_day = Date.beginning_of_week)
-
beginning_of_week(start_day)..end_of_week(start_day)
-
end
-
-
# Returns a Range representing the whole month of the current time.
-
1
def all_month
-
beginning_of_month..end_of_month
-
end
-
-
# Returns a Range representing the whole quarter of the current time.
-
1
def all_quarter
-
beginning_of_quarter..end_of_quarter
-
end
-
-
# Returns a Range representing the whole year of the current time.
-
1
def all_year
-
beginning_of_year..end_of_year
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
611
if ActiveSupport::Duration === other
-
other.until(self)
-
else
-
611
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Time#- can also be used to determine the number of seconds between two Time instances.
-
# We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
-
# are coerced into values that Time#- will recognize
-
1
def minus_with_coercion(other)
-
611
other = other.comparable_time if other.respond_to?(:comparable_time)
-
611
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
-
end
-
1
alias_method :minus_without_coercion, :-
-
1
alias_method :-, :minus_with_coercion
-
-
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
-
# can be chronologically compared with a Time
-
1
def compare_with_coercion(other)
-
# we're avoiding Time#to_datetime cause it's expensive
-
3
if other.is_a?(Time)
-
3
compare_without_coercion(other.to_time)
-
else
-
to_datetime <=> other
-
end
-
end
-
1
alias_method :compare_without_coercion, :<=>
-
1
alias_method :<=>, :compare_with_coercion
-
-
# Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
-
# can be eql? to an equivalent Time
-
1
def eql_with_coercion(other)
-
# if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
-
other = other.comparable_time if other.respond_to?(:comparable_time)
-
eql_without_coercion(other)
-
end
-
1
alias_method :eql_without_coercion, :eql?
-
1
alias_method :eql?, :eql_with_coercion
-
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/values/time_zone'
-
-
1
class Time
-
1
DATE_FORMATS = {
-
:db => '%Y-%m-%d %H:%M:%S',
-
:number => '%Y%m%d%H%M%S',
-
:nsec => '%Y%m%d%H%M%S%9N',
-
:time => '%H:%M',
-
:short => '%d %b %H:%M',
-
:long => '%B %d, %Y %H:%M',
-
:long_ordinal => lambda { |time|
-
day_format = ActiveSupport::Inflector.ordinalize(time.day)
-
time.strftime("%B #{day_format}, %Y %H:%M")
-
},
-
:rfc822 => lambda { |time|
-
offset_format = time.formatted_offset(false)
-
time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
-
}
-
}
-
-
# Converts to a formatted string. See DATE_FORMATS for builtin formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
-
#
-
# time.to_formatted_s(:time) # => "06:10"
-
# time.to_s(:time) # => "06:10"
-
#
-
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
-
# time.to_formatted_s(:number) # => "20070118061017"
-
# time.to_formatted_s(:short) # => "18 Jan 06:10"
-
# time.to_formatted_s(:long) # => "January 18, 2007 06:10"
-
# time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
-
# time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
-
#
-
# == Adding your own time formats to +to_formatted_s+
-
# You can add your own formats to the Time::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a time argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns the UTC offset as an +HH:MM formatted string.
-
#
-
# Time.local(2000).formatted_offset # => "-06:00"
-
# Time.local(2000).formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
end
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
-
# preserves utc_offset. Preserve zone also, even though it may not
-
# work in some edge cases.
-
1
if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
-
1
class Time
-
1
class << self
-
1
alias_method :_load_without_zone, :_load
-
1
def _load(marshaled_time)
-
time = _load_without_zone(marshaled_time)
-
time.instance_eval do
-
if zone = defined?(@_zone) && remove_instance_variable('@_zone')
-
ary = to_a
-
ary[0] += subsec if ary[0] == sec
-
ary[-1] = zone
-
utc? ? Time.utc(*ary) : Time.local(*ary)
-
else
-
self
-
end
-
end
-
end
-
end
-
-
1
alias_method :_dump_without_zone, :_dump
-
1
def _dump(*args)
-
obj = dup
-
obj.instance_variable_set('@_zone', zone)
-
obj._dump_without_zone(*args)
-
end
-
end
-
end
-
1
require 'active_support/time_with_zone'
-
-
1
class Time
-
1
@zone_default = nil
-
-
1
class << self
-
1
attr_accessor :zone_default
-
-
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
-
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
-
1
def zone
-
32
Thread.current[:time_zone] || zone_default
-
end
-
-
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
-
#
-
# This method accepts any of the following:
-
#
-
# * A Rails TimeZone object.
-
# * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
-
# * A TZInfo::Timezone object.
-
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
-
#
-
# Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
-
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
-
#
-
# class ApplicationController < ActionController::Base
-
# around_filter :set_time_zone
-
#
-
# def set_time_zone
-
# if logged_in?
-
# Time.use_zone(current_user.time_zone) { yield }
-
# else
-
# yield
-
# end
-
# end
-
# end
-
1
def zone=(time_zone)
-
Thread.current[:time_zone] = find_zone!(time_zone)
-
end
-
-
# Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
-
1
def use_zone(time_zone)
-
new_zone = find_zone!(time_zone)
-
begin
-
old_zone, ::Time.zone = ::Time.zone, new_zone
-
yield
-
ensure
-
::Time.zone = old_zone
-
end
-
end
-
-
# Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
-
1
def find_zone!(time_zone)
-
if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
-
time_zone
-
else
-
# lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
-
unless time_zone.respond_to?(:period_for_local)
-
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
-
end
-
-
# Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
-
if time_zone.is_a?(ActiveSupport::TimeZone)
-
time_zone
-
else
-
ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
-
end
-
end
-
rescue TZInfo::InvalidTimezoneIdentifier
-
raise ArgumentError, "Invalid Timezone: #{time_zone}"
-
end
-
-
1
def find_zone(time_zone)
-
find_zone!(time_zone) rescue nil
-
end
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
-
# instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
-
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
-
#
-
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
32
if zone
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
else
-
32
self
-
end
-
end
-
end
-
1
require "active_support/inflector/methods"
-
-
1
module ActiveSupport
-
# Autoload and eager load conveniences for your library.
-
#
-
# This module allows you to define autoloads based on
-
# Rails conventions (i.e. no need to define the path
-
# it is automatically guessed based on the filename)
-
# and also define a set of constants that needs to be
-
# eager loaded:
-
#
-
# module MyLib
-
# extend ActiveSupport::Autoload
-
#
-
# autoload :Model
-
#
-
# eager_autoload do
-
# autoload :Cache
-
# end
-
# end
-
#
-
# Then your library can be eager loaded by simply calling:
-
#
-
# MyLib.eager_load!
-
1
module Autoload
-
1
def self.extended(base) # :nodoc:
-
3
base.class_eval do
-
3
@_autoloads = {}
-
3
@_under_path = nil
-
3
@_at_path = nil
-
3
@_eager_autoload = false
-
end
-
end
-
-
1
def autoload(const_name, path = @_at_path)
-
55
unless path
-
47
full = [name, @_under_path, const_name.to_s, path].compact.join("::")
-
47
path = Inflector.underscore(full)
-
end
-
-
55
if @_eager_autoload
-
23
@_autoloads[const_name] = path
-
end
-
-
55
super const_name, path
-
end
-
-
1
def autoload_under(path)
-
@_under_path, old_path = path, @_under_path
-
yield
-
ensure
-
@_under_path = old_path
-
end
-
-
1
def autoload_at(path)
-
@_at_path, old_path = path, @_at_path
-
yield
-
ensure
-
@_at_path = old_path
-
end
-
-
1
def eager_autoload
-
3
old_eager, @_eager_autoload = @_eager_autoload, true
-
3
yield
-
ensure
-
3
@_eager_autoload = old_eager
-
end
-
-
1
def eager_load!
-
@_autoloads.values.each { |file| require file }
-
end
-
-
1
def autoloads
-
@_autoloads
-
end
-
end
-
end
-
1
require 'singleton'
-
-
1
module ActiveSupport
-
# \Deprecation specifies the API used by Rails to deprecate methods, instance
-
# variables, objects and constants.
-
1
class Deprecation
-
# active_support.rb sets an autoload for ActiveSupport::Deprecation.
-
#
-
# If these requires were at the top of the file the constant would not be
-
# defined by the time their files were loaded. Since some of them reopen
-
# ActiveSupport::Deprecation its autoload would be triggered, resulting in
-
# a circular require warning for active_support/deprecation.rb.
-
#
-
# So, we define the constant first, and load dependencies later.
-
1
require 'active_support/deprecation/instance_delegator'
-
1
require 'active_support/deprecation/behaviors'
-
1
require 'active_support/deprecation/reporting'
-
1
require 'active_support/deprecation/method_wrappers'
-
1
require 'active_support/deprecation/proxy_wrappers'
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
include Singleton
-
1
include InstanceDelegator
-
1
include Behavior
-
1
include Reporting
-
1
include MethodWrapper
-
-
# The version the deprecated behavior will be removed, by default.
-
1
attr_accessor :deprecation_horizon
-
-
# It accepts two parameters on initialization. The first is an version of library
-
# and the second is an library name
-
#
-
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
-
1
def initialize(deprecation_horizon = '4.1', gem_name = 'Rails')
-
1
self.gem_name = gem_name
-
1
self.deprecation_horizon = deprecation_horizon
-
# By default, warnings are not silenced and debugging is off.
-
1
self.silenced = false
-
1
self.debug = false
-
end
-
end
-
end
-
1
require "active_support/notifications"
-
-
1
module ActiveSupport
-
1
class Deprecation
-
# Default warning behaviors per Rails.env.
-
1
DEFAULT_BEHAVIORS = {
-
:stderr => Proc.new { |message, callstack|
-
$stderr.puts(message)
-
$stderr.puts callstack.join("\n ") if debug
-
},
-
:log => Proc.new { |message, callstack|
-
logger =
-
if defined?(Rails) && Rails.logger
-
Rails.logger
-
else
-
require 'active_support/logger'
-
ActiveSupport::Logger.new($stderr)
-
end
-
logger.warn message
-
logger.debug callstack.join("\n ") if debug
-
},
-
:notify => Proc.new { |message, callstack|
-
ActiveSupport::Notifications.instrument("deprecation.rails",
-
:message => message, :callstack => callstack)
-
},
-
:silence => Proc.new { |message, callstack| }
-
}
-
-
1
module Behavior
-
# Whether to print a backtrace along with the warning.
-
1
attr_accessor :debug
-
-
# Returns the current behavior or if one isn't set, defaults to +:stderr+.
-
1
def behavior
-
4
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
-
end
-
-
# Sets the behavior to the specified value. Can be a single value, array,
-
# or an object that responds to +call+.
-
#
-
# Available behaviors:
-
#
-
# [+stderr+] Log all deprecation warnings to +$stderr+.
-
# [+log+] Log all deprecation warnings to +Rails.logger+.
-
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
-
# [+silence+] Do nothing.
-
#
-
# Setting behaviors only affects deprecations that happen after boot time.
-
# Deprecation warnings raised by gems are not affected by this setting
-
# because they happen before Rails boots up.
-
#
-
# ActiveSupport::Deprecation.behavior = :stderr
-
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
-
# ActiveSupport::Deprecation.behavior = MyCustomHandler
-
# ActiveSupport::Deprecation.behavior = proc { |message, callstack|
-
# # custom stuff
-
# }
-
1
def behavior=(behavior)
-
8
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module InstanceDelegator
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
1
base.public_class_method :new
-
end
-
-
1
module ClassMethods
-
1
def include(included_module)
-
15
included_module.instance_methods.each { |m| method_added(m) }
-
3
super
-
end
-
-
1
def method_added(method_name)
-
15
singleton_class.delegate(method_name, to: :instance)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module MethodWrapper
-
# Declare that a method has been deprecated.
-
#
-
# module Fred
-
# extend self
-
#
-
# def foo; end
-
# def bar; end
-
# def baz; end
-
# end
-
#
-
# ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
-
# # => [:foo, :bar, :baz]
-
#
-
# Fred.foo
-
# # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
-
#
-
# Fred.bar
-
# # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
-
#
-
# Fred.baz
-
# # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
-
1
def deprecate_methods(target_module, *method_names)
-
options = method_names.extract_options!
-
deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance
-
method_names += options.keys
-
-
method_names.each do |method_name|
-
target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
-
target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
-
deprecator.deprecation_warning(method_name, options[method_name])
-
send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
class DeprecationProxy #:nodoc:
-
1
def self.new(*args, &block)
-
object = args.first
-
-
return object unless object
-
super
-
end
-
-
66
instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
-
-
# Don't give a deprecation warning on inspect since test/unit and error
-
# logs rely on it for diagnostics.
-
1
def inspect
-
target.inspect
-
end
-
-
1
private
-
1
def method_missing(called, *args, &block)
-
warn caller, called, args
-
target.__send__(called, *args, &block)
-
end
-
end
-
-
# This DeprecatedObjectProxy transforms object to depracated object.
-
#
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
-
#
-
# When someone execute any method expect +inspect+ on proxy object this will
-
# trigger +warn+ method on +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>
-
1
class DeprecatedObjectProxy < DeprecationProxy
-
1
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
-
@object = object
-
@message = message
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@object
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn(@message, callstack)
-
end
-
end
-
-
# This DeprecatedInstanceVariableProxy transforms instance variable to
-
# depracated instance variable.
-
#
-
# class Example
-
# def initialize(deprecator)
-
# @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
-
# @_request = :a_request
-
# end
-
#
-
# def request
-
# @_request
-
# end
-
#
-
# def old_request
-
# @request
-
# end
-
# end
-
#
-
# When someone execute any method on @request variable this will trigger
-
# +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
-
# variable via +request+ method and execute the same method on non-proxy
-
# instance variable.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedInstanceVariableProxy < DeprecationProxy
-
1
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
-
@instance = instance
-
@method = method
-
@var = var
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@instance.__send__(@method)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
-
end
-
end
-
-
# This DeprecatedConstantProxy transforms constant to depracated constant.
-
#
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
-
#
-
# When someone use old constant this will trigger +warn+ method on
-
# +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedConstantProxy < DeprecationProxy
-
1
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
-
@old_const = old_const
-
@new_const = new_const
-
@deprecator = deprecator
-
end
-
-
1
def class
-
target.class
-
end
-
-
1
private
-
1
def target
-
ActiveSupport::Inflector.constantize(@new_const.to_s)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module Reporting
-
# Whether to print a message (silent mode)
-
1
attr_accessor :silenced
-
# Name of gem where method is deprecated
-
1
attr_accessor :gem_name
-
-
# Outputs a deprecation warning to the output configured by
-
# <tt>ActiveSupport::Deprecation.behavior</tt>.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
1
def warn(message = nil, callstack = nil)
-
2
return if silenced
-
-
2
callstack ||= caller(2)
-
2
deprecation_message(callstack, message).tap do |m|
-
4
behavior.each { |b| b.call(m, callstack) }
-
end
-
end
-
-
# Silence deprecation warnings within the block.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
#
-
# ActiveSupport::Deprecation.silence do
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# end
-
# # => nil
-
1
def silence
-
old_silenced, @silenced = @silenced, true
-
yield
-
ensure
-
@silenced = old_silenced
-
end
-
-
1
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
-
caller_backtrace ||= caller(2)
-
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
-
warn(msg, caller_backtrace)
-
end
-
end
-
-
1
private
-
# Outputs a deprecation warning message
-
#
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
-
1
def deprecated_method_warning(method_name, message = nil)
-
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
-
case message
-
when Symbol then "#{warning} (use #{message} instead)"
-
when String then "#{warning} (#{message})"
-
else warning
-
end
-
end
-
-
1
def deprecation_message(callstack, message = nil)
-
2
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
-
2
message += '.' unless message =~ /\.$/
-
2
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
-
end
-
-
1
def deprecation_caller_message(callstack)
-
2
file, line, method = extract_callstack(callstack)
-
2
if file
-
2
if line && method
-
2
"(called from #{method} at #{file}:#{line})"
-
else
-
"(called from #{file}:#{line})"
-
end
-
end
-
end
-
-
1
def extract_callstack(callstack)
-
2
rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
-
22
offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
-
2
if offending_line
-
2
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
-
2
md.captures
-
else
-
offending_line
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# This module provides an internal implementation to track descendants
-
# which is faster than iterating through ObjectSpace.
-
1
module DescendantsTracker
-
1
@@direct_descendants = {}
-
-
1
class << self
-
1
def direct_descendants(klass)
-
@@direct_descendants[klass] || []
-
end
-
-
1
def descendants(klass)
-
1169
arr = []
-
1169
accumulate_descendants(klass, arr)
-
1169
arr
-
end
-
-
1
def clear
-
if defined? ActiveSupport::Dependencies
-
@@direct_descendants.each do |klass, descendants|
-
if ActiveSupport::Dependencies.autoloaded?(klass)
-
@@direct_descendants.delete(klass)
-
else
-
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
-
end
-
end
-
else
-
@@direct_descendants.clear
-
end
-
end
-
-
# This is the only method that is not thread safe, but is only ever called
-
# during the eager loading phase.
-
1
def store_inherited(klass, descendant)
-
60
(@@direct_descendants[klass] ||= []) << descendant
-
end
-
-
1
private
-
1
def accumulate_descendants(klass, acc)
-
2419
if direct_descendants = @@direct_descendants[klass]
-
1017
acc.concat(direct_descendants)
-
2267
direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
-
end
-
end
-
end
-
-
1
def inherited(base)
-
60
DescendantsTracker.store_inherited(self, base)
-
60
super
-
end
-
-
1
def direct_descendants
-
DescendantsTracker.direct_descendants(self)
-
end
-
-
1
def descendants
-
222
DescendantsTracker.descendants(self)
-
end
-
end
-
end
-
1
require 'active_support/basic_object'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# Provides accurate date and time measurements using Date#advance and
-
# Time#advance, respectively. It mainly supports the methods on Numeric.
-
#
-
# 1.month.ago # equivalent to Time.now.advance(months: -1)
-
1
class Duration < BasicObject
-
1
attr_accessor :value, :parts
-
-
1
def initialize(value, parts) #:nodoc:
-
@value, @parts = value, parts
-
end
-
-
# Adds another Duration or a Numeric to this Duration. Numeric values
-
# are treated as seconds.
-
1
def +(other)
-
if Duration === other
-
Duration.new(value + other.value, @parts + other.parts)
-
else
-
Duration.new(value + other, @parts + [[:seconds, other]])
-
end
-
end
-
-
# Subtracts another Duration or a Numeric from this Duration. Numeric
-
# values are treated as seconds.
-
1
def -(other)
-
self + (-other)
-
end
-
-
1
def -@ #:nodoc:
-
Duration.new(-value, parts.map { |type,number| [type, -number] })
-
end
-
-
1
def is_a?(klass) #:nodoc:
-
Duration == klass || value.is_a?(klass)
-
end
-
1
alias :kind_of? :is_a?
-
-
# Returns +true+ if +other+ is also a Duration instance with the
-
# same +value+, or if <tt>other == value</tt>.
-
1
def ==(other)
-
if Duration === other
-
other.value == value
-
else
-
other == value
-
end
-
end
-
-
1
def self.===(other) #:nodoc:
-
611
other.is_a?(Duration)
-
rescue ::NoMethodError
-
false
-
end
-
-
# Calculates a new Time or Date that is as far in the future
-
# as this Duration represents.
-
1
def since(time = ::Time.current)
-
sum(1, time)
-
end
-
1
alias :from_now :since
-
-
# Calculates a new Time or Date that is as far in the past
-
# as this Duration represents.
-
1
def ago(time = ::Time.current)
-
sum(-1, time)
-
end
-
1
alias :until :ago
-
-
1
def inspect #:nodoc:
-
consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
-
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
-
n = consolidated[length]
-
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
-
end.compact
-
parts = ["0 seconds"] if parts.empty?
-
parts.to_sentence(:locale => :en)
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_i
-
end
-
-
1
protected
-
-
1
def sum(sign, time = ::Time.current) #:nodoc:
-
parts.inject(time) do |t,(type,number)|
-
if t.acts_like?(:time) || t.acts_like?(:date)
-
if type == :seconds
-
t.since(sign * number)
-
else
-
t.advance(type => sign * number)
-
end
-
else
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
-
end
-
end
-
end
-
-
1
private
-
-
1
def method_missing(method, *args, &block) #:nodoc:
-
value.send(method, *args, &block)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
-
# to be the same.
-
#
-
# rgb = ActiveSupport::HashWithIndifferentAccess.new
-
#
-
# rgb[:black] = '#000000'
-
# rgb[:black] # => '#000000'
-
# rgb['black'] # => '#000000'
-
#
-
# rgb['white'] = '#FFFFFF'
-
# rgb[:white] # => '#FFFFFF'
-
# rgb['white'] # => '#FFFFFF'
-
#
-
# Internally symbols are mapped to strings when used as keys in the entire
-
# writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
-
# mapping belongs to the public interface. For example, given:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
#
-
# You are guaranteed that the key is returned as a string:
-
#
-
# hash.keys # => ["a"]
-
#
-
# Technically other types of keys are accepted:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
# hash[0] = 0
-
# hash # => {"a"=>1, 0=>0}
-
#
-
# but this class is intended for use cases where strings or symbols are the
-
# expected keys and it is convenient to understand both as the same. For
-
# example the +params+ hash in Ruby on Rails.
-
#
-
# Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
-
#
-
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
-
#
-
# which may be handy.
-
1
class HashWithIndifferentAccess < Hash
-
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
-
# this class.
-
1
def extractable_options?
-
true
-
end
-
-
1
def with_indifferent_access
-
dup
-
end
-
-
1
def nested_under_indifferent_access
-
self
-
end
-
-
1
def initialize(constructor = {})
-
18
if constructor.is_a?(Hash)
-
18
super()
-
18
update(constructor)
-
else
-
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
10
if key.is_a?(Symbol) && include?(key = key.to_s)
-
1
self[key]
-
else
-
9
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
new(hash).tap do |new_hash|
-
new_hash.default = hash.default
-
end
-
end
-
-
1
def self.[](*args)
-
8
new.merge(Hash[*args])
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:key] = 'value'
-
#
-
# This value can be later fetched using either +:key+ or +'key'+.
-
1
def []=(key, value)
-
regular_writer(convert_key(key), convert_value(value))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the receiver in-place, merging in the hash passed as argument:
-
#
-
# hash_1 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_1[:key] = 'value'
-
#
-
# hash_2 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_2[:key] = 'New Value!'
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
# The argument can be either an
-
# <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
-
# In either case the merge respects the semantics of indifferent access.
-
#
-
# If the argument is a regular hash with keys +:key+ and +"key"+ only one
-
# of the values end up in the receiver, but which one is unspecified.
-
#
-
# When given a block, the value for duplicated keys will be determined
-
# by the result of invoking the block with the duplicated key, the value
-
# in the receiver, and the value in +other_hash+. The rules for duplicated
-
# keys follow the semantics of indifferent access:
-
#
-
# hash_1[:key] = 10
-
# hash_2['key'] = 12
-
# hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
-
1
def update(other_hash)
-
26
if other_hash.is_a? HashWithIndifferentAccess
-
8
super(other_hash)
-
else
-
18
other_hash.each_pair do |key, value|
-
9
if block_given? && key?(key)
-
value = yield(convert_key(key), self[key], value)
-
end
-
9
regular_writer(convert_key(key), convert_value(value))
-
end
-
18
self
-
end
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['key'] = 'value'
-
# hash.key?(:key) # => true
-
# hash.key?('key') # => true
-
1
def key?(key)
-
1
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
-
# either a string or a symbol:
-
#
-
# counters = ActiveSupport::HashWithIndifferentAccess.new
-
# counters[:foo] = 1
-
#
-
# counters.fetch('foo') # => 1
-
# counters.fetch(:bar, 0) # => 0
-
# counters.fetch(:bar) {|key| 0} # => 0
-
# counters.fetch(:zoo) # => KeyError: key not found: "zoo"
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:a] = 'x'
-
# hash[:b] = 'y'
-
# hash.values_at('a', 'b') # => ["x", "y"]
-
1
def values_at(*indices)
-
indices.collect {|key| self[convert_key(key)]}
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
8
self.class.new(self).tap do |new_hash|
-
8
new_hash.default = default
-
end
-
end
-
-
# This method has the same semantics of +update+, except it does not
-
# modify the receiver but rather returns a new hash with indifferent
-
# access with the result of the merge.
-
1
def merge(hash, &block)
-
8
self.dup.update(hash, &block)
-
end
-
-
# Like +merge+ but the other way around: Merges the receiver into the
-
# argument and returns a new hash with indifferent access as result:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['a'] = nil
-
# hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
-
1
def reverse_merge(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Replaces the contents of this hash with other_hash.
-
#
-
# h = { "a" => 100, "b" => 200 }
-
# h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
-
1
def replace(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Removes the specified key from the hash.
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def deep_stringify_keys!; self end
-
1
def stringify_keys; dup end
-
1
def deep_stringify_keys; dup end
-
1
undef :symbolize_keys!
-
1
undef :deep_symbolize_keys!
-
1
def symbolize_keys; to_hash.symbolize_keys end
-
1
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
-
1
def to_options!; self end
-
-
# Convert to a regular hash with string keys.
-
1
def to_hash
-
Hash.new(default).merge!(self)
-
end
-
-
1
protected
-
1
def convert_key(key)
-
10
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value)
-
23
if value.is_a? Hash
-
value.nested_under_indifferent_access
-
23
elsif value.is_a?(Array)
-
7
value = value.dup if value.frozen?
-
21
value.map! { |e| convert_value(e) }
-
else
-
16
value
-
end
-
end
-
end
-
end
-
-
1
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
-
1
begin
-
1
require 'i18n'
-
1
require 'active_support/lazy_load_hooks'
-
rescue LoadError => e
-
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
-
raise e
-
end
-
-
1
ActiveSupport.run_load_hooks(:i18n)
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
-
1
require 'active_support/inflector/inflections'
-
-
1
module ActiveSupport
-
1
Inflector.inflections(:en) do |inflect|
-
1
inflect.plural(/$/, 's')
-
1
inflect.plural(/s$/i, 's')
-
1
inflect.plural(/^(ax|test)is$/i, '\1es')
-
1
inflect.plural(/(octop|vir)us$/i, '\1i')
-
1
inflect.plural(/(octop|vir)i$/i, '\1i')
-
1
inflect.plural(/(alias|status)$/i, '\1es')
-
1
inflect.plural(/(bu)s$/i, '\1ses')
-
1
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
-
1
inflect.plural(/([ti])um$/i, '\1a')
-
1
inflect.plural(/([ti])a$/i, '\1a')
-
1
inflect.plural(/sis$/i, 'ses')
-
1
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
-
1
inflect.plural(/(hive)$/i, '\1s')
-
1
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
-
1
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
-
1
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
-
1
inflect.plural(/^(m|l)ouse$/i, '\1ice')
-
1
inflect.plural(/^(m|l)ice$/i, '\1ice')
-
1
inflect.plural(/^(ox)$/i, '\1en')
-
1
inflect.plural(/^(oxen)$/i, '\1')
-
1
inflect.plural(/(quiz)$/i, '\1zes')
-
-
1
inflect.singular(/s$/i, '')
-
1
inflect.singular(/(ss)$/i, '\1')
-
1
inflect.singular(/(n)ews$/i, '\1ews')
-
1
inflect.singular(/([ti])a$/i, '\1um')
-
1
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/([^f])ves$/i, '\1fe')
-
1
inflect.singular(/(hive)s$/i, '\1')
-
1
inflect.singular(/(tive)s$/i, '\1')
-
1
inflect.singular(/([lr])ves$/i, '\1f')
-
1
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
-
1
inflect.singular(/(s)eries$/i, '\1eries')
-
1
inflect.singular(/(m)ovies$/i, '\1ovie')
-
1
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
-
1
inflect.singular(/^(m|l)ice$/i, '\1ouse')
-
1
inflect.singular(/(bus)(es)?$/i, '\1')
-
1
inflect.singular(/(o)es$/i, '\1')
-
1
inflect.singular(/(shoe)s$/i, '\1')
-
1
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
-
1
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
-
1
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
-
1
inflect.singular(/(alias|status)(es)?$/i, '\1')
-
1
inflect.singular(/^(ox)en/i, '\1')
-
1
inflect.singular(/(vert|ind)ices$/i, '\1ex')
-
1
inflect.singular(/(matr)ices$/i, '\1ix')
-
1
inflect.singular(/(quiz)zes$/i, '\1')
-
1
inflect.singular(/(database)s$/i, '\1')
-
-
1
inflect.irregular('person', 'people')
-
1
inflect.irregular('man', 'men')
-
1
inflect.irregular('child', 'children')
-
1
inflect.irregular('sex', 'sexes')
-
1
inflect.irregular('move', 'moves')
-
1
inflect.irregular('cow', 'kine')
-
1
inflect.irregular('zombie', 'zombies')
-
-
1
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
-
end
-
end
-
# in case active_support/inflector is required without the rest of active_support
-
1
require 'active_support/inflector/inflections'
-
1
require 'active_support/inflector/transliterate'
-
1
require 'active_support/inflector/methods'
-
-
1
require 'active_support/inflections'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/array/prepend_and_append'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
1
extend self
-
-
# A singleton instance of this class is yielded by Inflector.inflections,
-
# which can then be used to specify additional inflection rules. If passed
-
# an optional locale, rules for other languages can be specified. The
-
# default locale is <tt>:en</tt>. Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.plural /^(ox)$/i, '\1\2en'
-
# inflect.singular /^(ox)en/i, '\1'
-
#
-
# inflect.irregular 'octopus', 'octopi'
-
#
-
# inflect.uncountable 'equipment'
-
# end
-
#
-
# New rules are added at the top. So in the example above, the irregular
-
# rule for octopus will now be the first of the pluralization and
-
# singularization rules that is runs. This guarantees that your rules run
-
# before any of the rules that may already have been loaded.
-
1
class Inflections
-
1
def self.instance(locale = :en)
-
2227
@__instance__ ||= Hash.new { |h, k| h[k] = new }
-
2226
@__instance__[locale]
-
end
-
-
1
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
-
-
1
def initialize
-
1
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
-
end
-
-
# Private, for the test suite.
-
1
def initialize_dup(orig) # :nodoc:
-
%w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
-
instance_variable_set("@#{scope}", orig.send(scope).dup)
-
end
-
end
-
-
# Specifies a new acronym. An acronym must be specified as it will appear
-
# in a camelized string. An underscore string that contains the acronym
-
# will retain the acronym when passed to +camelize+, +humanize+, or
-
# +titleize+. A camelized string that contains the acronym will maintain
-
# the acronym when titleized or humanized, and will convert the acronym
-
# into a non-delimited single lowercase word when passed to +underscore+.
-
#
-
# acronym 'HTML'
-
# titleize 'html' #=> 'HTML'
-
# camelize 'html' #=> 'HTML'
-
# underscore 'MyHTML' #=> 'my_html'
-
#
-
# The acronym, however, must occur as a delimited unit and not be part of
-
# another word for conversions to recognize it:
-
#
-
# acronym 'HTTP'
-
# camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
-
# camelize 'https' #=> 'Https', not 'HTTPs'
-
# underscore 'HTTPS' #=> 'http_s', not 'https'
-
#
-
# acronym 'HTTPS'
-
# camelize 'https' #=> 'HTTPS'
-
# underscore 'HTTPS' #=> 'https'
-
#
-
# Note: Acronyms that are passed to +pluralize+ will no longer be
-
# recognized, since the acronym will not occur as a delimited unit in the
-
# pluralized result. To work around this, you must specify the pluralized
-
# form as an acronym as well:
-
#
-
# acronym 'API'
-
# camelize(pluralize('api')) #=> 'Apis'
-
#
-
# acronym 'APIs'
-
# camelize(pluralize('api')) #=> 'APIs'
-
#
-
# +acronym+ may be used to specify any word that contains an acronym or
-
# otherwise needs to maintain a non-standard capitalization. The only
-
# restriction is that the word must begin with a capital letter.
-
#
-
# acronym 'RESTful'
-
# underscore 'RESTful' #=> 'restful'
-
# underscore 'RESTfulController' #=> 'restful_controller'
-
# titleize 'RESTfulController' #=> 'RESTful Controller'
-
# camelize 'restful' #=> 'RESTful'
-
# camelize 'restful_controller' #=> 'RESTfulController'
-
#
-
# acronym 'McDonald'
-
# underscore 'McDonald' #=> 'mcdonald'
-
# camelize 'mcdonald' #=> 'McDonald'
-
1
def acronym(word)
-
@acronyms[word.downcase] = word
-
@acronym_regex = /#{@acronyms.values.join("|")}/
-
end
-
-
# Specifies a new pluralization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def plural(rule, replacement)
-
37
@uncountables.delete(rule) if rule.is_a?(String)
-
37
@uncountables.delete(replacement)
-
37
@plurals.prepend([rule, replacement])
-
end
-
-
# Specifies a new singularization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def singular(rule, replacement)
-
35
@uncountables.delete(rule) if rule.is_a?(String)
-
35
@uncountables.delete(replacement)
-
35
@singulars.prepend([rule, replacement])
-
end
-
-
# Specifies a new irregular that applies to both pluralization and
-
# singularization at the same time. This can only be used for strings, not
-
# regular expressions. You simply pass the irregular in singular and
-
# plural form.
-
#
-
# irregular 'octopus', 'octopi'
-
# irregular 'person', 'people'
-
1
def irregular(singular, plural)
-
7
@uncountables.delete(singular)
-
7
@uncountables.delete(plural)
-
7
if singular[0,1].upcase == plural[0,1].upcase
-
6
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
-
6
plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
-
6
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
-
else
-
1
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
-
1
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
-
1
plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
-
1
plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
-
1
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
-
1
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
-
end
-
end
-
-
# Add uncountable words that shouldn't be attempted inflected.
-
#
-
# uncountable 'money'
-
# uncountable 'money', 'information'
-
# uncountable %w( money information rice )
-
1
def uncountable(*words)
-
1
(@uncountables << words).flatten!
-
end
-
-
# Specifies a humanized form of a string by a regular expression rule or
-
# by a string mapping. When using a regular expression based replacement,
-
# the normal humanize formatting is called after the replacement. When a
-
# string is used, the human form should be specified as desired (example:
-
# 'The name', not 'the_name').
-
#
-
# human /_cnt$/i, '\1_count'
-
# human 'legacy_col_person_name', 'Name'
-
1
def human(rule, replacement)
-
@humans.prepend([rule, replacement])
-
end
-
-
# Clears the loaded inflections within a given scope (default is
-
# <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
-
# options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
-
# <tt>:humans</tt>.
-
#
-
# clear :all
-
# clear :plurals
-
1
def clear(scope = :all)
-
case scope
-
when :all
-
@plurals, @singulars, @uncountables, @humans = [], [], [], []
-
else
-
instance_variable_set "@#{scope}", []
-
end
-
end
-
end
-
-
# Yields a singleton instance of Inflector::Inflections so you can specify
-
# additional inflector rules. If passed an optional locale, rules for other
-
# languages can be specified. If not specified, defaults to <tt>:en</tt>.
-
# Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.uncountable 'rails'
-
# end
-
1
def inflections(locale = :en)
-
2226
if block_given?
-
1
yield Inflections.instance(locale)
-
else
-
2225
Inflections.instance(locale)
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/inflector/inflections'
-
1
require 'active_support/inflections'
-
-
1
module ActiveSupport
-
# The Inflector transforms words from singular to plural, class names to table
-
# names, modularized class names to ones without, and class names to foreign
-
# keys. The default inflections for pluralization, singularization, and
-
# uncountable words are kept in inflections.rb.
-
#
-
# The Rails core team has stated patches for the inflections library will not
-
# be accepted in order to avoid breaking legacy applications which may be
-
# relying on errant inflections. If you discover an incorrect inflection and
-
# require it for your application or wish to define rules for languages other
-
# than English, please correct or add them yourself (explained below).
-
1
module Inflector
-
1
extend self
-
-
# Returns the plural form of the word in the string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# pluralized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'ley'.pluralize(:es) # => "leyes"
-
1
def pluralize(word, locale = :en)
-
115
apply_inflections(word, inflections(locale).plurals)
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a
-
# string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# pluralized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(word, locale = :en)
-
57
apply_inflections(word, inflections(locale).singulars)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument
-
# to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
-
# lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting
-
# paths to namespaces.
-
#
-
# 'active_model'.camelize # => "ActiveModel"
-
# 'active_model'.camelize(:lower) # => "activeModel"
-
# 'active_model/errors'.camelize # => "ActiveModel::Errors"
-
# 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
-
#
-
# As a rule of thumb you can think of +camelize+ as the inverse of
-
# +underscore+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def camelize(term, uppercase_first_letter = true)
-
126
string = term.to_s
-
126
if uppercase_first_letter
-
230
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
-
else
-
22
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
-
end
-
163
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
-
end
-
-
# Makes an underscored, lowercase form from the expression in the string.
-
#
-
# Changes '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
#
-
# As a rule of thumb you can think of +underscore+ as the inverse of
-
# +camelize+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def underscore(camel_cased_word)
-
276
word = camel_cased_word.to_s.dup
-
276
word.gsub!('::', '/')
-
276
word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
-
276
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
-
276
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
-
276
word.tr!("-", "_")
-
276
word.downcase!
-
276
word
-
end
-
-
# Capitalizes the first word and turns underscores into spaces and strips a
-
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty
-
# output.
-
#
-
# 'employee_salary'.humanize # => "Employee salary"
-
# 'author_id'.humanize # => "Author"
-
1
def humanize(lower_case_and_underscored_word)
-
460
result = lower_case_and_underscored_word.to_s.dup
-
460
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
460
result.gsub!(/_id$/, "")
-
460
result.tr!('_', ' ')
-
460
result.gsub(/([a-z\d]*)/i) { |match|
-
982
"#{inflections.acronyms[match] || match.downcase}"
-
460
}.gsub(/^\w/) { $&.upcase }
-
end
-
-
# Capitalizes all the words and replaces some characters in the string to
-
# create a nicer looking title. +titleize+ is meant for creating pretty
-
# output. It is not used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
# 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
-
# 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
-
1
def titleize(word)
-
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
-
end
-
-
# Create the name of a table like Rails does for models to table names. This
-
# method uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize(class_name)
-
55
pluralize(underscore(class_name))
-
end
-
-
# Create a class name from a plural table name like Rails does for table
-
# names to models. Note that this returns a string and not a Class (To
-
# convert to an actual class follow +classify+ with +constantize+).
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
#
-
# Singular names are not handled correctly:
-
#
-
# 'business'.classify # => "Busines"
-
1
def classify(table_name)
-
# strip out any leading schema name
-
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize(underscored_word)
-
underscored_word.tr('_', '-')
-
end
-
-
# Removes the module part from the expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize(path)
-
55
path = path.to_s
-
55
if i = path.rindex('::')
-
31
path[(i+2)..-1]
-
else
-
24
path
-
end
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize(path)
-
path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
-
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.constantize # => Module
-
# 'Test::Unit'.constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.constantize # => 'outside', same as ::C
-
# end
-
#
-
# NameError is raised when the name is not in CamelCase or the constant is
-
# unknown.
-
1
def constantize(camel_cased_word)
-
51
names = camel_cased_word.split('::')
-
51
names.shift if names.empty? || names.first.empty?
-
-
51
names.inject(Object) do |constant, name|
-
52
if constant == Object
-
51
constant.const_get(name)
-
else
-
1
candidate = constant.const_get(name)
-
1
next candidate if constant.const_defined?(name, false)
-
next candidate unless Object.const_defined?(name)
-
-
# Go down the ancestors to check it it's owned
-
# directly before we reach Object or the end of ancestors.
-
constant = constant.ancestors.inject do |const, ancestor|
-
break const if ancestor == Object
-
break ancestor if ancestor.const_defined?(name, false)
-
const
-
end
-
-
# owner is in Object, so raise
-
constant.const_get(name, false)
-
end
-
end
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Test::Unit'.safe_constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.safe_constantize # => 'outside', same as ::C
-
# end
-
#
-
# +nil+ is returned when the name is not in CamelCase or the constant (or
-
# part of it) is unknown.
-
#
-
# 'blargle'.safe_constantize # => nil
-
# 'UnknownModule'.safe_constantize # => nil
-
# 'UnknownModule::Foo::Bar'.safe_constantize # => nil
-
1
def safe_constantize(camel_cased_word)
-
begin
-
constantize(camel_cased_word)
-
rescue NameError => e
-
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
-
e.name.to_s == camel_cased_word.to_s
-
rescue ArgumentError => e
-
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
-
end
-
end
-
-
# Returns the suffix that should be added to a number to denote the position
-
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinal(1) # => "st"
-
# ordinal(2) # => "nd"
-
# ordinal(1002) # => "nd"
-
# ordinal(1003) # => "rd"
-
# ordinal(-11) # => "th"
-
# ordinal(-1021) # => "st"
-
1
def ordinal(number)
-
abs_number = number.to_i.abs
-
-
if (11..13).include?(abs_number % 100)
-
"th"
-
else
-
case abs_number % 10
-
when 1; "st"
-
when 2; "nd"
-
when 3; "rd"
-
else "th"
-
end
-
end
-
end
-
-
# Turns a number into an ordinal string used to denote the position in an
-
# ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinalize(1) # => "1st"
-
# ordinalize(2) # => "2nd"
-
# ordinalize(1002) # => "1002nd"
-
# ordinalize(1003) # => "1003rd"
-
# ordinalize(-11) # => "-11th"
-
# ordinalize(-1021) # => "-1021st"
-
1
def ordinalize(number)
-
"#{number}#{ordinal(number)}"
-
end
-
-
1
private
-
-
# Mount a regular expression that will match part by part of the constant.
-
# For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
-
1
def const_regexp(camel_cased_word) #:nodoc:
-
parts = camel_cased_word.split("::")
-
last = parts.pop
-
-
parts.reverse.inject(last) do |acc, part|
-
part.empty? ? acc : "#{part}(::#{acc})?"
-
end
-
end
-
-
# Applies inflection rules for +singularize+ and +pluralize+.
-
#
-
# apply_inflections('post', inflections.plurals) # => "posts"
-
# apply_inflections('posts', inflections.singulars) # => "post"
-
1
def apply_inflections(word, rules)
-
172
result = word.to_s.dup
-
-
172
if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
-
3
result
-
else
-
6014
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
169
result
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/core_ext/string/multibyte'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
-
# Replaces non-ASCII characters with an ASCII approximation, or if none
-
# exists, a replacement character which defaults to "?".
-
#
-
# transliterate('Ærøskøbing')
-
# # => "AEroskobing"
-
#
-
# Default approximations are provided for Western/Latin characters,
-
# e.g, "ø", "ñ", "é", "ß", etc.
-
#
-
# This method is I18n aware, so you can set up custom approximations for a
-
# locale. This can be useful, for example, to transliterate German's "ü"
-
# and "ö" to "ue" and "oe", or to add support for transliterating Russian
-
# to ASCII.
-
#
-
# In order to make your custom transliterations available, you must set
-
# them as the <tt>i18n.transliterate.rule</tt> i18n key:
-
#
-
# # Store the transliterations in locales/de.yml
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# # Or set them using Ruby
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: {
-
# 'ü' => 'ue',
-
# 'ö' => 'oe'
-
# }
-
# }
-
# })
-
#
-
# The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
-
# maps characters to ASCII approximations as shown above, or, for more
-
# complex requirements, a Proc:
-
#
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: ->(string) { MyTransliterator.transliterate(string) }
-
# }
-
# })
-
#
-
# Now you can have different transliterations for each locale:
-
#
-
# I18n.locale = :en
-
# transliterate('Jürgen')
-
# # => "Jurgen"
-
#
-
# I18n.locale = :de
-
# transliterate('Jürgen')
-
# # => "Juergen"
-
1
def transliterate(string, replacement = "?")
-
I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
-
ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
-
:replacement => replacement)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of
-
# a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path(@person)) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(string, sep = '-')
-
# replace accented chars with their ascii equivalents
-
parameterized_string = transliterate(string)
-
# Turn unwanted chars into the separator
-
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
-
unless sep.nil? || sep.empty?
-
re_sep = Regexp.escape(sep)
-
# No more than one of the separator in a row.
-
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
-
# Remove leading/trailing separator.
-
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
-
end
-
parameterized_string.downcase
-
end
-
-
end
-
end
-
1
require 'active_support/json/decoding'
-
1
require 'active_support/json/encoding'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'multi_json'
-
-
1
module ActiveSupport
-
# Look for and parse json strings that look like ISO 8601 times.
-
1
mattr_accessor :parse_json_times
-
-
1
module JSON
-
1
class << self
-
# Parses a JSON string (JavaScript Object Notation) into a hash.
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
-
# => {"team" => "rails", "players" => "36"}
-
1
def decode(json, options ={})
-
3
data = MultiJson.load(json, options)
-
3
if ActiveSupport.parse_json_times
-
convert_dates_from(data)
-
else
-
3
data
-
end
-
end
-
-
1
def engine
-
MultiJson.adapter
-
end
-
1
alias :backend :engine
-
-
1
def engine=(name)
-
MultiJson.use(name)
-
end
-
1
alias :backend= :engine=
-
-
1
def with_backend(name)
-
old_backend, self.backend = backend, name
-
yield
-
ensure
-
self.backend = old_backend
-
end
-
-
# Returns the class of the error that will be raised when there is an
-
# error in decoding JSON. Using this method means you won't directly
-
# depend on the ActiveSupport's JSON implementation, in case it changes
-
# in the future.
-
#
-
# begin
-
# obj = ActiveSupport::JSON.decode(some_string)
-
# rescue ActiveSupport::JSON.parse_error
-
# Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
-
# end
-
1
def parse_error
-
MultiJson::DecodeError
-
end
-
-
1
private
-
-
1
def convert_dates_from(data)
-
case data
-
when nil
-
nil
-
when DATE_REGEX
-
begin
-
DateTime.parse(data)
-
rescue ArgumentError
-
data
-
end
-
when Array
-
data.map! { |d| convert_dates_from(d) }
-
when Hash
-
data.each do |key, value|
-
data[key] = convert_dates_from(value)
-
end
-
else
-
data
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/to_json'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/json/variable'
-
-
1
require 'bigdecimal'
-
1
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/instance_variables'
-
1
require 'time'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'set'
-
-
1
module ActiveSupport
-
1
class << self
-
1
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
-
:escape_html_entities_in_json, :escape_html_entities_in_json=,
-
:encode_big_decimal_as_string, :encode_big_decimal_as_string=,
-
:to => :'ActiveSupport::JSON::Encoding'
-
end
-
-
1
module JSON
-
# matches YAML-formatted dates
-
1
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
-
# Dumps objects in JSON (JavaScript Object Notation).
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
-
1
def self.encode(value, options = nil)
-
27
Encoding::Encoder.new(options).encode(value)
-
end
-
-
1
module Encoding #:nodoc:
-
1
class CircularReferenceError < StandardError; end
-
-
1
class Encoder
-
1
attr_reader :options
-
-
1
def initialize(options = nil)
-
53
@options = options || {}
-
53
@seen = Set.new
-
end
-
-
1
def encode(value, use_options = true)
-
187
check_for_circular_references(value) do
-
187
jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
-
187
jsonified.encode_json(self)
-
end
-
end
-
-
# like encode, but only calls as_json, without encoding to string.
-
1
def as_json(value, use_options = true)
-
53
check_for_circular_references(value) do
-
53
use_options ? value.as_json(options_for(value)) : value.as_json
-
end
-
end
-
-
1
def options_for(value)
-
116
if value.is_a?(Array) || value.is_a?(Hash)
-
# hashes and arrays need to get encoder in the options, so that
-
# they can detect circular references.
-
6
options.merge(:encoder => self)
-
else
-
110
options
-
end
-
end
-
-
1
def escape(string)
-
136
Encoding.escape(string)
-
end
-
-
1
private
-
1
def check_for_circular_references(value)
-
240
unless @seen.add?(value.__id__)
-
raise CircularReferenceError, 'object references itself'
-
end
-
240
yield
-
ensure
-
240
@seen.delete(value.__id__)
-
end
-
end
-
-
-
1
ESCAPED_CHARS = {
-
"\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
-
"\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
-
"\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
-
"\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
-
"\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
-
"\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
-
"\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
-
"\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
-
"\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
-
"\010" => '\b',
-
"\f" => '\f',
-
"\n" => '\n',
-
"\r" => '\r',
-
"\t" => '\t',
-
'"' => '\"',
-
'\\' => '\\\\',
-
'>' => '\u003E',
-
'<' => '\u003C',
-
'&' => '\u0026' }
-
-
1
class << self
-
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
-
# to the Active Support legacy format.
-
1
attr_accessor :use_standard_json_time_format
-
-
# If false, serializes BigDecimal objects as numeric instead of wrapping
-
# them in a string.
-
1
attr_accessor :encode_big_decimal_as_string
-
-
1
attr_accessor :escape_regex
-
1
attr_reader :escape_html_entities_in_json
-
-
1
def escape_html_entities_in_json=(value)
-
1
self.escape_regex = \
-
if @escape_html_entities_in_json = value
-
1
/[\x00-\x1F"\\><&]/
-
else
-
/[\x00-\x1F"\\]/
-
end
-
end
-
-
1
def escape(string)
-
136
string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
-
136
json = string.
-
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
-
gsub(/([\xC0-\xDF][\x80-\xBF]|
-
[\xE0-\xEF][\x80-\xBF]{2}|
-
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
-
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
-
}
-
136
json = %("#{json}")
-
136
json.force_encoding(::Encoding::UTF_8)
-
136
json
-
end
-
end
-
-
1
self.use_standard_json_time_format = true
-
1
self.escape_html_entities_in_json = true
-
1
self.encode_big_decimal_as_string = true
-
end
-
end
-
end
-
-
1
class Object
-
1
def as_json(options = nil) #:nodoc:
-
if respond_to?(:to_hash)
-
to_hash
-
else
-
instance_values
-
end
-
end
-
end
-
-
1
class Struct #:nodoc:
-
1
def as_json(options = nil)
-
Hash[members.zip(values)]
-
end
-
end
-
-
1
class TrueClass
-
1
def as_json(options = nil) #:nodoc:
-
14
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
10
to_s
-
end
-
end
-
-
1
class FalseClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class NilClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
'null'
-
end
-
end
-
-
1
class String
-
1
def as_json(options = nil) #:nodoc:
-
144
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
136
encoder.escape(self)
-
end
-
end
-
-
1
class Symbol
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Numeric
-
1
def as_json(options = nil) #:nodoc:
-
14
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
10
to_s
-
end
-
end
-
-
1
class Float
-
# Encoding Infinity or NaN to JSON should return "null". The default returns
-
# "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
-
1
def as_json(options = nil) #:nodoc:
-
finite? ? self : nil
-
end
-
end
-
-
1
class BigDecimal
-
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
-
# however, parse non-integer JSON numbers directly as floats. Clients using
-
# those libraries would get in general a wrong number and no way to recover
-
# other than manually inspecting the string with the JSON code itself.
-
#
-
# That's why a JSON string is returned. The JSON literal is not numeric, but
-
# if the other end knows by contract that the data is supposed to be a
-
# BigDecimal, it still has the chance to post-process the string and get the
-
# real value.
-
#
-
# Use <tt>ActiveSupport.use_standard_json_big_decimal_format = true</tt> to
-
# override this behaviour.
-
1
def as_json(options = nil) #:nodoc:
-
if finite?
-
ActiveSupport.encode_big_decimal_as_string ? to_s : self
-
else
-
nil
-
end
-
end
-
end
-
-
1
class Regexp
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
module Enumerable
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
end
-
-
1
class Range
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Array
-
1
def as_json(options = nil) #:nodoc:
-
# use encoder as a proxy to call as_json on all elements, to protect from circular references
-
12
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
27
map { |v| encoder.as_json(v, options) }
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
# we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
-
18
"[#{map { |v| v.encode_json(encoder) } * ','}]"
-
end
-
end
-
-
1
class Hash
-
1
def as_json(options = nil) #:nodoc:
-
# create a subset of the hash by applying :only or :except
-
20
subset = if options
-
2
if attrs = options[:only]
-
slice(*Array(attrs))
-
elsif attrs = options[:except]
-
except(*Array(attrs))
-
else
-
2
self
-
end
-
else
-
18
self
-
end
-
-
# use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
-
20
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
58
Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
# values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
-
# processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
-
-
# on the other hand, we need to run as_json on the elements, because the model representation may contain fields
-
# like Time/Date in their original (not jsonified) form, etc.
-
-
113
"{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
-
end
-
end
-
-
1
class Time
-
1
def as_json(options = nil) #:nodoc:
-
19
if ActiveSupport.use_standard_json_time_format
-
19
xmlschema
-
else
-
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
end
-
-
1
class Date
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
strftime("%Y-%m-%d")
-
else
-
strftime("%Y/%m/%d")
-
end
-
end
-
end
-
-
1
class DateTime
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
xmlschema
-
else
-
strftime('%Y/%m/%d %H:%M:%S %z')
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module JSON
-
# Deprecated: A string that returns itself as its JSON-encoded form.
-
1
class Variable < String
-
1
def initialize(*args)
-
message = 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
-
'For your own custom JSON literals, define #as_json and #encode_json yourself.'
-
ActiveSupport::Deprecation.warn message
-
super
-
end
-
-
1
def as_json(options = nil) self end #:nodoc:
-
1
def encode_json(encoder) self end #:nodoc:
-
end
-
end
-
end
-
1
module ActiveSupport
-
# lazy_load_hooks allows rails to lazily load a lot of components and thus
-
# making the app boot faster. Because of this feature now there is no need to
-
# require <tt>ActiveRecord::Base</tt> at boot time purely to apply
-
# configuration. Instead a hook is registered that applies configuration once
-
# <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
-
# used as example but this feature can be applied elsewhere too.
-
#
-
# Here is an example where +on_load+ method is called to register a hook.
-
#
-
# initializer 'active_record.initialize_timezone' do
-
# ActiveSupport.on_load(:active_record) do
-
# self.time_zone_aware_attributes = true
-
# self.default_timezone = :utc
-
# end
-
# end
-
#
-
# When the entirety of +activerecord/lib/active_record/base.rb+ has been
-
# evaluated then +run_load_hooks+ is invoked. The very last line of
-
# +activerecord/lib/active_record/base.rb+ is:
-
#
-
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
-
2
@load_hooks = Hash.new { |h,k| h[k] = [] }
-
2
@loaded = Hash.new { |h,k| h[k] = [] }
-
-
1
def self.on_load(name, options = {}, &block)
-
1
@loaded[name].each do |base|
-
1
execute_hook(base, options, block)
-
end
-
-
1
@load_hooks[name] << [block, options]
-
end
-
-
1
def self.execute_hook(base, options, block)
-
1
if options[:yield]
-
block.call(base)
-
else
-
1
base.instance_eval(&block)
-
end
-
end
-
-
1
def self.run_load_hooks(name, base = Object)
-
1
@loaded[name] << base
-
1
@load_hooks[name].each do |hook, options|
-
execute_hook(base, options, hook)
-
end
-
end
-
end
-
1
require 'logger'
-
-
1
module ActiveSupport
-
1
class Logger < ::Logger
-
# Broadcasts logs to multiple loggers.
-
1
def self.broadcast(logger) # :nodoc:
-
Module.new do
-
define_method(:add) do |*args, &block|
-
logger.add(*args, &block)
-
super(*args, &block)
-
end
-
-
define_method(:<<) do |x|
-
logger << x
-
super(x)
-
end
-
-
define_method(:close) do
-
logger.close
-
super()
-
end
-
-
define_method(:progname=) do |name|
-
logger.progname = name
-
super(name)
-
end
-
-
define_method(:formatter=) do |formatter|
-
logger.formatter = formatter
-
super(formatter)
-
end
-
-
define_method(:level=) do |level|
-
logger.level = level
-
super(level)
-
end
-
end
-
end
-
-
1
def initialize(*args)
-
super
-
@formatter = SimpleFormatter.new
-
end
-
-
# Simple formatter which only displays the message.
-
1
class SimpleFormatter < ::Logger::Formatter
-
# This method is invoked when a log event occurs
-
1
def call(severity, timestamp, progname, msg)
-
"#{String === msg ? msg : msg.inspect}\n"
-
end
-
end
-
end
-
end
-
1
module ActiveSupport #:nodoc:
-
1
module Multibyte
-
1
autoload :Chars, 'active_support/multibyte/chars'
-
1
autoload :Unicode, 'active_support/multibyte/unicode'
-
-
# The proxy class returned when calling mb_chars. You can use this accessor
-
# to configure your own proxy class so you can support other encodings. See
-
# the ActiveSupport::Multibyte::Chars implementation for an example how to
-
# do this.
-
#
-
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
-
1
def self.proxy_class=(klass)
-
@proxy_class = klass
-
end
-
-
# Returns the current proxy class.
-
1
def self.proxy_class
-
@proxy_class ||= ActiveSupport::Multibyte::Chars
-
end
-
end
-
end
-
1
require 'active_support/notifications/instrumenter'
-
1
require 'active_support/notifications/fanout'
-
-
1
module ActiveSupport
-
# = Notifications
-
#
-
# <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
-
# Ruby.
-
#
-
# == Instrumenters
-
#
-
# To instrument an event you just need to do:
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# That executes the block first and notifies all subscribers once done.
-
#
-
# In the example above +render+ is the name of the event, and the rest is called
-
# the _payload_. The payload is a mechanism that allows instrumenters to pass
-
# extra information to subscribers. Payloads consist of a hash whose contents
-
# are arbitrary and generally depend on the event.
-
#
-
# == Subscribers
-
#
-
# You can consume those events and the information they provide by registering
-
# a subscriber.
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
-
# name # => String, name of the event (such as 'render' from above)
-
# start # => Time, when the instrumented block started execution
-
# finish # => Time, when the instrumented block ended execution
-
# id # => String, unique ID for this notification
-
# payload # => Hash, the payload
-
# end
-
#
-
# For instance, let's store all "render" events in an array:
-
#
-
# events = []
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |*args|
-
# events << ActiveSupport::Notifications::Event.new(*args)
-
# end
-
#
-
# That code returns right away, you are just subscribing to "render" events.
-
# The block is saved and will be called whenever someone instruments "render":
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# event = events.first
-
# event.name # => "render"
-
# event.duration # => 10 (in milliseconds)
-
# event.payload # => { extra: :information }
-
#
-
# The block in the <tt>subscribe</tt> call gets the name of the event, start
-
# timestamp, end timestamp, a string with a unique identifier for that event
-
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
-
# that order.
-
#
-
# If an exception happens during that particular instrumentation the payload will
-
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
-
# the name of the exception class, and the exception message.
-
#
-
# As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
-
# is able to take the arguments as they come and provide an object-oriented
-
# interface to that data.
-
#
-
# It is also possible to pass an object as the second parameter passed to the
-
# <tt>subscribe</tt> method instead of a block:
-
#
-
# module ActionController
-
# class PageRequest
-
# def call(name, started, finished, unique_id, payload)
-
# Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
-
# end
-
# end
-
# end
-
#
-
# ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
-
#
-
# resulting in the following output within the logs including a hash with the payload:
-
#
-
# notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
-
# :controller=>"Devise::SessionsController",
-
# :action=>"new",
-
# :params=>{"action"=>"new", "controller"=>"devise/sessions"},
-
# :format=>:html,
-
# :method=>"GET",
-
# :path=>"/login/sign_in",
-
# :status=>200,
-
# :view_runtime=>279.3080806732178,
-
# :db_runtime=>40.053
-
# }
-
#
-
# You can also subscribe to all events whose name matches a certain regexp:
-
#
-
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
-
# ...
-
# end
-
#
-
# and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
-
# to all events.
-
#
-
# == Temporary Subscriptions
-
#
-
# Sometimes you do not want to subscribe to an event for the entire life of
-
# the application. There are two ways to unsubscribe.
-
#
-
# WARNING: The instrumentation framework is designed for long-running subscribers,
-
# use this feature sparingly because it wipes some internal caches and that has
-
# a negative impact on performance.
-
#
-
# === Subscribe While a Block Runs
-
#
-
# You can subscribe to some event temporarily while some block runs. For
-
# example, in
-
#
-
# callback = lambda {|*args| ... }
-
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
-
# ...
-
# end
-
#
-
# the callback will be called for all "sql.active_record" events instrumented
-
# during the execution of the block. The callback is unsubscribed automatically
-
# after that.
-
#
-
# === Manual Unsubscription
-
#
-
# The +subscribe+ method returns a subscriber object:
-
#
-
# subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
-
# ...
-
# end
-
#
-
# To prevent that block from being called anymore, just unsubscribe passing
-
# that reference:
-
#
-
# ActiveSupport::Notifications.unsubscribe(subscriber)
-
#
-
# == Default Queue
-
#
-
# Notifications ships with a queue implementation that consumes and publish events
-
# to log subscribers in a thread. You can use any queue implementation you want.
-
#
-
1
module Notifications
-
1
class << self
-
1
attr_accessor :notifier
-
-
1
def publish(name, *args)
-
notifier.publish(name, *args)
-
end
-
-
1
def instrument(name, payload = {})
-
if notifier.listening?(name)
-
instrumenter.instrument(name, payload) { yield payload if block_given? }
-
else
-
yield payload if block_given?
-
end
-
end
-
-
1
def subscribe(*args, &block)
-
notifier.subscribe(*args, &block)
-
end
-
-
1
def subscribed(callback, *args, &block)
-
subscriber = subscribe(*args, &callback)
-
yield
-
ensure
-
unsubscribe(subscriber)
-
end
-
-
1
def unsubscribe(args)
-
notifier.unsubscribe(args)
-
end
-
-
1
def instrumenter
-
Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
-
end
-
end
-
-
1
self.notifier = Fanout.new
-
end
-
end
-
1
require 'mutex_m'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# This is a default queue implementation that ships with Notifications.
-
# It just pushes events to all registered log subscribers.
-
#
-
# This class is thread safe. All methods are reentrant.
-
1
class Fanout
-
1
include Mutex_m
-
-
1
def initialize
-
1
@subscribers = []
-
1
@listeners_for = {}
-
1
super
-
end
-
-
1
def subscribe(pattern = nil, block = Proc.new)
-
subscriber = Subscribers.new pattern, block
-
synchronize do
-
@subscribers << subscriber
-
@listeners_for.clear
-
end
-
subscriber
-
end
-
-
1
def unsubscribe(subscriber)
-
synchronize do
-
@subscribers.reject! { |s| s.matches?(subscriber) }
-
@listeners_for.clear
-
end
-
end
-
-
1
def start(name, id, payload)
-
listeners_for(name).each { |s| s.start(name, id, payload) }
-
end
-
-
1
def finish(name, id, payload)
-
listeners_for(name).each { |s| s.finish(name, id, payload) }
-
end
-
-
1
def publish(name, *args)
-
listeners_for(name).each { |s| s.publish(name, *args) }
-
end
-
-
1
def listeners_for(name)
-
synchronize do
-
@listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
-
end
-
end
-
-
1
def listening?(name)
-
listeners_for(name).any?
-
end
-
-
# This is a sync queue, so there is no waiting.
-
1
def wait
-
end
-
-
1
module Subscribers # :nodoc:
-
1
def self.new(pattern, listener)
-
if listener.respond_to?(:start) and listener.respond_to?(:finish)
-
subscriber = Evented.new pattern, listener
-
else
-
subscriber = Timed.new pattern, listener
-
end
-
-
unless pattern
-
AllMessages.new(subscriber)
-
else
-
subscriber
-
end
-
end
-
-
1
class Evented #:nodoc:
-
1
def initialize(pattern, delegate)
-
@pattern = pattern
-
@delegate = delegate
-
end
-
-
1
def start(name, id, payload)
-
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
@delegate.finish name, id, payload
-
end
-
-
1
def subscribed_to?(name)
-
@pattern === name.to_s
-
end
-
-
1
def matches?(subscriber_or_name)
-
self === subscriber_or_name ||
-
@pattern && @pattern === subscriber_or_name
-
end
-
end
-
-
1
class Timed < Evented
-
1
def initialize(pattern, delegate)
-
@timestack = []
-
super
-
end
-
-
1
def publish(name, *args)
-
@delegate.call name, *args
-
end
-
-
1
def start(name, id, payload)
-
@timestack.push Time.now
-
end
-
-
1
def finish(name, id, payload)
-
started = @timestack.pop
-
@delegate.call(name, started, Time.now, id, payload)
-
end
-
end
-
-
1
class AllMessages # :nodoc:
-
1
def initialize(delegate)
-
@delegate = delegate
-
end
-
-
1
def start(name, id, payload)
-
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
@delegate.finish name, id, payload
-
end
-
-
1
def publish(name, *args)
-
@delegate.publish name, *args
-
end
-
-
1
def subscribed_to?(name)
-
true
-
end
-
-
1
alias :matches? :===
-
end
-
end
-
end
-
end
-
end
-
1
require 'securerandom'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# Instrumentors are stored in a thread local.
-
1
class Instrumenter
-
1
attr_reader :id
-
-
1
def initialize(notifier)
-
@id = unique_id
-
@notifier = notifier
-
end
-
-
# Instrument the given block by measuring the time taken to execute it
-
# and publish it. Notice that events get sent even if an error occurs
-
# in the passed-in block.
-
1
def instrument(name, payload={})
-
@notifier.start(name, @id, payload)
-
begin
-
yield
-
rescue Exception => e
-
payload[:exception] = [e.class.name, e.message]
-
raise e
-
ensure
-
@notifier.finish(name, @id, payload)
-
end
-
end
-
-
1
private
-
1
def unique_id
-
SecureRandom.hex(10)
-
end
-
end
-
-
1
class Event
-
1
attr_reader :name, :time, :transaction_id, :payload, :children
-
1
attr_accessor :end
-
-
1
def initialize(name, start, ending, transaction_id, payload)
-
@name = name
-
@payload = payload.dup
-
@time = start
-
@transaction_id = transaction_id
-
@end = ending
-
@children = []
-
end
-
-
1
def duration
-
1000.0 * (self.end - time)
-
end
-
-
1
def <<(event)
-
@children << event
-
end
-
-
1
def parent_of?(event)
-
@children.include? event
-
end
-
end
-
end
-
end
-
# This is private interface.
-
#
-
# Rails components cherry pick from Active Support as needed, but there are a
-
# few features that are used for sure some way or another and it is not worth
-
# to put individual requires absolutely everywhere. Think blank? for example.
-
#
-
# This file is loaded by every Rails component except Active Support itself,
-
# but it does not belong to the Rails public interface. It is internal to
-
# Rails and can change anytime.
-
-
# Defines Object#blank? and Object#present?.
-
1
require 'active_support/core_ext/object/blank'
-
-
# Rails own autoload, eager_load, etc.
-
1
require 'active_support/dependencies/autoload'
-
-
# Support for ClassMethods and the included macro.
-
1
require 'active_support/concern'
-
-
# Defines Class#class_attribute.
-
1
require 'active_support/core_ext/class/attribute'
-
-
# Defines Module#delegate.
-
1
require 'active_support/core_ext/module/delegation'
-
-
# Defines ActiveSupport::Deprecation.
-
1
require 'active_support/deprecation'
-
1
gem 'minitest' # make sure we get the gem, not stdlib
-
1
require 'minitest/spec'
-
1
require 'active_support/testing/tagged_logging'
-
1
require 'active_support/testing/setup_and_teardown'
-
1
require 'active_support/testing/assertions'
-
1
require 'active_support/testing/deprecation'
-
1
require 'active_support/testing/isolation'
-
1
require 'active_support/testing/mocha_module'
-
1
require 'active_support/testing/constant_lookup'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
class TestCase < ::MiniTest::Spec
-
-
1
include ActiveSupport::Testing::MochaModule
-
-
# Use AS::TestCase for the base class when describing a model
-
1
register_spec_type(self) do |desc|
-
Class === desc && desc < ActiveRecord::Base
-
end
-
-
1
Assertion = MiniTest::Assertion
-
1
alias_method :method_name, :__name__
-
-
1
$tags = {}
-
1
def self.for_tag(tag)
-
yield if $tags[tag]
-
end
-
-
# FIXME: we have tests that depend on run order, we should fix that and
-
# remove this method.
-
1
def self.test_order # :nodoc:
-
82
:sorted
-
end
-
-
1
include ActiveSupport::Testing::TaggedLogging
-
1
include ActiveSupport::Testing::SetupAndTeardown
-
1
include ActiveSupport::Testing::Assertions
-
1
include ActiveSupport::Testing::Deprecation
-
-
1
def self.describe(text)
-
if block_given?
-
super
-
else
-
message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n"
-
ActiveSupport::Deprecation.warn message
-
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def self.name
-
"#{text}"
-
end
-
RUBY_EVAL
-
end
-
end
-
-
1
class << self
-
1
alias :test :it
-
end
-
-
# test/unit backwards compatibility methods
-
1
alias :assert_raise :assert_raises
-
1
alias :assert_not_nil :refute_nil
-
1
alias :assert_not_equal :refute_equal
-
1
alias :assert_no_match :refute_match
-
1
alias :assert_not_same :refute_same
-
-
# Fails if the block raises an exception.
-
#
-
# assert_nothing_raised do
-
# ...
-
# end
-
1
def assert_nothing_raised(*args)
-
9
yield
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Assertions
-
# Test numeric difference between the return value of an expression as a
-
# result of what is evaluated in the yielded block.
-
#
-
# assert_difference 'Article.count' do
-
# post :create, article: {...}
-
# end
-
#
-
# An arbitrary expression is passed in and evaluated.
-
#
-
# assert_difference 'assigns(:article).comments(:reload).size' do
-
# post :create, comment: {...}
-
# end
-
#
-
# An arbitrary positive or negative difference can be specified.
-
# The default is <tt>1</tt>.
-
#
-
# assert_difference 'Article.count', -1 do
-
# post :delete, id: ...
-
# end
-
#
-
# An array of expressions can also be passed in and evaluated.
-
#
-
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# A lambda or a list of lambdas can be passed in and evaluated:
-
#
-
# assert_difference ->{ Article.count }, 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
-
# post :delete, id: ...
-
# end
-
1
def assert_difference(expression, difference = 1, message = nil, &block)
-
expressions = Array(expression)
-
-
exps = expressions.map { |e|
-
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
-
}
-
before = exps.map { |e| e.call }
-
-
yield
-
-
expressions.zip(exps).each_with_index do |(code, e), i|
-
error = "#{code.inspect} didn't change by #{difference}"
-
error = "#{message}.\n#{error}" if message
-
assert_equal(before[i] + difference, e.call, error)
-
end
-
end
-
-
# Assertion that the numeric result of evaluating an expression is not
-
# changed before and after invoking the passed in block.
-
#
-
# assert_no_difference 'Article.count' do
-
# post :create, article: invalid_attributes
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_no_difference 'Article.count', 'An Article should not be created' do
-
# post :create, article: invalid_attributes
-
# end
-
1
def assert_no_difference(expression, message = nil, &block)
-
assert_difference expression, 0, message, &block
-
end
-
-
# Test if an expression is blank. Passes if <tt>object.blank?</tt>
-
# is +true+.
-
#
-
# assert_blank [] # => true
-
# assert_blank [[]] # => [[]] is not blank
-
#
-
# An error message can be specified.
-
#
-
# assert_blank [], 'this should be blank'
-
1
def assert_blank(object, message=nil)
-
message ||= "#{object.inspect} is not blank"
-
assert object.blank?, message
-
end
-
-
# Test if an expression is not blank. Passes if <tt>object.present?</tt>
-
# is +true+.
-
#
-
# assert_present({ data: 'x' }) # => true
-
# assert_present({}) # => {} is blank
-
#
-
# An error message can be specified.
-
#
-
# assert_present({ data: 'x' }, 'this should not be blank')
-
1
def assert_present(object, message=nil)
-
message ||= "#{object.inspect} is blank"
-
assert object.present?, message
-
end
-
end
-
end
-
end
-
1
require "active_support/concern"
-
1
require "active_support/inflector"
-
-
1
module ActiveSupport
-
1
module Testing
-
# Resolves a constant from a minitest spec name.
-
#
-
# Given the following spec-style test:
-
#
-
# describe WidgetsController, :index do
-
# describe "authenticated user" do
-
# describe "returns widgets" do
-
# it "has a controller that exists" do
-
# assert_kind_of WidgetsController, @controller
-
# end
-
# end
-
# end
-
# end
-
#
-
# The test will have the following name:
-
#
-
# "WidgetsController::index::authenticated user::returns widgets"
-
#
-
# The constant WidgetsController can be resolved from the name.
-
# The following code will resolve the constant:
-
#
-
# controller = determine_constant_from_test_name(name) do |constant|
-
# Class === constant && constant < ::ActionController::Metal
-
# end
-
1
module ConstantLookup
-
1
extend ::ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def determine_constant_from_test_name(test_name)
-
names = test_name.split "::"
-
while names.size > 0 do
-
names.last.sub!(/Test$/, "")
-
begin
-
constant = names.join("::").constantize
-
break(constant) if yield(constant)
-
rescue NameError
-
# Constant wasn't found, move on
-
ensure
-
names.pop
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Deprecation #:nodoc:
-
1
def assert_deprecated(match = nil, &block)
-
2
result, warnings = collect_deprecations(&block)
-
2
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
-
2
if match
-
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
-
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
-
end
-
2
result
-
end
-
-
1
def assert_not_deprecated(&block)
-
result, deprecations = collect_deprecations(&block)
-
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
-
result
-
end
-
-
1
private
-
1
def collect_deprecations
-
2
old_behavior = ActiveSupport::Deprecation.behavior
-
2
deprecations = []
-
2
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
-
2
deprecations << message
-
end
-
2
result = yield
-
2
[result, deprecations]
-
ensure
-
2
ActiveSupport::Deprecation.behavior = old_behavior
-
end
-
end
-
end
-
end
-
1
require 'rbconfig'
-
1
module ActiveSupport
-
1
module Testing
-
1
class RemoteError < StandardError
-
-
1
attr_reader :message, :backtrace
-
-
1
def initialize(exception)
-
@message = "caught #{exception.class.name}: #{exception.message}"
-
@backtrace = exception.backtrace
-
end
-
end
-
-
1
class ProxyTestResult
-
1
def initialize
-
@calls = []
-
end
-
-
1
def add_error(e)
-
e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
-
@calls << [:add_error, e]
-
end
-
-
1
def __replay__(result)
-
@calls.each do |name, args|
-
result.send(name, *args)
-
end
-
end
-
-
1
def method_missing(name, *args)
-
@calls << [name, args]
-
end
-
end
-
-
1
module Isolation
-
1
require 'thread'
-
-
1
class ParallelEach
-
1
include Enumerable
-
-
# default to 2 cores
-
1
CORES = (ENV['TEST_CORES'] || 2).to_i
-
-
1
def initialize list
-
@list = list
-
@queue = SizedQueue.new CORES
-
end
-
-
1
def grep pattern
-
self.class.new super
-
end
-
-
1
def each
-
threads = CORES.times.map {
-
Thread.new {
-
while job = @queue.pop
-
yield job
-
end
-
}
-
}
-
@list.each { |i| @queue << i }
-
CORES.times { @queue << nil }
-
threads.each(&:join)
-
end
-
end
-
-
1
def self.included(klass) #:nodoc:
-
klass.extend(Module.new {
-
def test_methods
-
ParallelEach.new super
-
end
-
})
-
end
-
-
1
def self.forking_env?
-
1
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
-
end
-
-
1
def _run_class_setup # class setup method should only happen in parent
-
unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
-
self.class.setup if self.class.respond_to?(:setup)
-
@@ran_class_setup = true
-
end
-
end
-
-
1
def run(runner)
-
_run_class_setup
-
-
serialized = run_in_isolation do |isolated_runner|
-
super(isolated_runner)
-
end
-
-
retval, proxy = Marshal.load(serialized)
-
proxy.__replay__(runner)
-
retval
-
end
-
-
1
module Forking
-
1
def run_in_isolation(&blk)
-
read, write = IO.pipe
-
-
pid = fork do
-
read.close
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
write.puts [Marshal.dump([retval, proxy])].pack("m")
-
exit!
-
end
-
-
write.close
-
result = read.read
-
Process.wait2(pid)
-
return result.unpack("m")[0]
-
end
-
end
-
-
1
module Subprocess
-
1
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
-
-
# Crazy H4X to get this working in windows / jruby with
-
# no forking.
-
1
def run_in_isolation(&blk)
-
require "tempfile"
-
-
if ENV["ISOLATION_TEST"]
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
-
file.puts [Marshal.dump([retval, proxy])].pack("m")
-
end
-
exit!
-
else
-
Tempfile.open("isolation") do |tmpfile|
-
ENV["ISOLATION_TEST"] = @method_name
-
ENV["ISOLATION_OUTPUT"] = tmpfile.path
-
-
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
-
`#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
-
-
ENV.delete("ISOLATION_TEST")
-
ENV.delete("ISOLATION_OUTPUT")
-
-
return tmpfile.read.unpack("m")[0]
-
end
-
end
-
end
-
end
-
-
1
include forking_env? ? Forking : Subprocess
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module MochaModule
-
1
begin
-
1
require 'mocha/api'
-
1
include Mocha::API
-
-
1
def before_setup
-
610
mocha_setup
-
610
super
-
end
-
-
1
def after_teardown
-
610
super
-
610
mocha_verify
-
610
mocha_teardown
-
end
-
rescue LoadError
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/callbacks'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module SetupAndTeardown
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :setup, :teardown
-
end
-
-
1
module ClassMethods
-
1
def setup(*args, &block)
-
4
set_callback(:setup, :before, *args, &block)
-
end
-
-
1
def teardown(*args, &block)
-
2
set_callback(:teardown, :after, *args, &block)
-
end
-
end
-
-
1
def before_setup
-
610
super
-
610
run_callbacks :setup
-
end
-
-
1
def after_teardown
-
610
run_callbacks :teardown
-
610
super
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module TaggedLogging
-
1
attr_writer :tagged_logger
-
-
1
def before_setup
-
610
tagged_logger.push_tags(self.class.name, __name__) if tagged_logging?
-
610
super
-
end
-
-
1
def after_teardown
-
610
super
-
610
tagged_logger.pop_tags(2) if tagged_logging?
-
end
-
-
1
private
-
1
def tagged_logger
-
1220
@tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
-
end
-
-
1
def tagged_logging?
-
1220
tagged_logger && tagged_logger.respond_to?(:push_tags)
-
end
-
end
-
end
-
end
-
1
require 'active_support'
-
-
1
module ActiveSupport
-
1
autoload :Duration, 'active_support/duration'
-
1
autoload :TimeWithZone, 'active_support/time_with_zone'
-
1
autoload :TimeZone, 'active_support/values/time_zone'
-
end
-
-
1
require 'date'
-
1
require 'time'
-
-
1
require 'active_support/core_ext/time/marshal'
-
1
require 'active_support/core_ext/time/acts_like'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
require 'active_support/core_ext/date/acts_like'
-
1
require 'active_support/core_ext/date/calculations'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/date/zones'
-
-
1
require 'active_support/core_ext/date_time/acts_like'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date_time/zones'
-
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require 'active_support/values/time_zone'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# A Time-like class that can represent a time in any time zone. Necessary
-
# because standard Ruby Time instances are limited to UTC and the
-
# system's <tt>ENV['TZ']</tt> zone.
-
#
-
# You shouldn't ever need to create a TimeWithZone instance directly via +new+.
-
# Instead use methods +local+, +parse+, +at+ and +now+ on TimeZone instances,
-
# and +in_time_zone+ on Time and DateTime instances.
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
-
# Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
#
-
# See Time and TimeZone for further documentation of these methods.
-
#
-
# TimeWithZone instances implement the same API as Ruby Time instances, so
-
# that Time and TimeWithZone instances are interchangeable.
-
#
-
# t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
-
# t.hour # => 13
-
# t.dst? # => true
-
# t.utc_offset # => -14400
-
# t.zone # => "EDT"
-
# t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
-
# t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
-
# t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
-
# t > Time.utc(1999) # => true
-
# t.is_a?(Time) # => true
-
# t.is_a?(ActiveSupport::TimeWithZone) # => true
-
1
class TimeWithZone
-
-
# Report class name as 'Time' to thwart type checking.
-
1
def self.name
-
'Time'
-
end
-
-
1
include Comparable
-
1
attr_reader :time_zone
-
-
1
def initialize(utc_time, time_zone, local_time = nil, period = nil)
-
@utc, @time_zone, @time = utc_time, time_zone, local_time
-
@period = @utc ? period : get_period_and_ensure_valid_local_time
-
end
-
-
# Returns a Time or DateTime instance that represents the time in +time_zone+.
-
1
def time
-
@time ||= period.to_local(@utc)
-
end
-
-
# Returns a Time or DateTime instance that represents the time in UTC.
-
1
def utc
-
@utc ||= period.to_utc(@time)
-
end
-
1
alias_method :comparable_time, :utc
-
1
alias_method :getgm, :utc
-
1
alias_method :getutc, :utc
-
1
alias_method :gmtime, :utc
-
-
# Returns the underlying TZInfo::TimezonePeriod.
-
1
def period
-
@period ||= time_zone.period_for_utc(@utc)
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
-
1
def in_time_zone(new_zone = ::Time.zone)
-
return self if time_zone == new_zone
-
utc.in_time_zone(new_zone)
-
end
-
-
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
-
# system's <tt>ENV['TZ']</tt> zone.
-
1
def localtime
-
utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
-
end
-
1
alias_method :getlocal, :localtime
-
-
1
def dst?
-
period.dst?
-
end
-
1
alias_method :isdst, :dst?
-
-
1
def utc?
-
time_zone.name == 'UTC'
-
end
-
1
alias_method :gmt?, :utc?
-
-
1
def utc_offset
-
period.utc_total_offset
-
end
-
1
alias_method :gmt_offset, :utc_offset
-
1
alias_method :gmtoff, :utc_offset
-
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Time uses +zone+ to display the time zone abbreviation, so we're
-
# duck-typing it.
-
1
def zone
-
period.zone_identifier.to_s
-
end
-
-
1
def inspect
-
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
-
end
-
-
1
def xmlschema(fraction_digits = 0)
-
fraction = if fraction_digits > 0
-
(".%06i" % time.usec)[0, fraction_digits + 1]
-
end
-
-
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
-
end
-
1
alias_method :iso8601, :xmlschema
-
-
# Coerces time to a string for JSON encoding. The default format is ISO 8601.
-
# You can get %Y/%m/%d %H:%M:%S +offset style by setting
-
# <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
-
# to +false+.
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005-02-01T15:15:10Z"
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005/02/01 15:15:10 +0000"
-
1
def as_json(options = nil)
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema
-
else
-
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
-
1
def encode_with(coder)
-
if coder.respond_to?(:represent_object)
-
coder.represent_object(nil, utc)
-
else
-
coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
-
end
-
end
-
-
1
def httpdate
-
utc.httpdate
-
end
-
-
1
def rfc2822
-
to_s(:rfc822)
-
end
-
1
alias_method :rfc822, :rfc2822
-
-
# <tt>:db</tt> format outputs time in UTC; all others output time in local.
-
# Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
-
1
def to_s(format = :default)
-
if format == :db
-
utc.to_s(format)
-
elsif formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
-
end
-
end
-
1
alias_method :to_formatted_s, :to_s
-
-
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
-
# +formatted_offset+, respectively, before passing to Time#strftime, so
-
# that zone information is correct
-
1
def strftime(format)
-
format = format.gsub('%Z', zone)
-
.gsub('%z', formatted_offset(false))
-
.gsub('%:z', formatted_offset(true))
-
.gsub('%::z', formatted_offset(true) + ":00")
-
time.strftime(format)
-
end
-
-
# Use the time in UTC for comparisons.
-
1
def <=>(other)
-
utc <=> other
-
end
-
-
1
def between?(min, max)
-
utc.between?(min, max)
-
end
-
-
1
def past?
-
utc.past?
-
end
-
-
1
def today?
-
time.today?
-
end
-
-
1
def future?
-
utc.future?
-
end
-
-
1
def eql?(other)
-
utc.eql?(other)
-
end
-
-
1
def hash
-
utc.hash
-
end
-
-
1
def +(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:+, other)
-
else
-
result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def -(other)
-
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
-
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
-
if other.acts_like?(:time)
-
utc.to_f - other.to_f
-
elsif duration_of_variable_length?(other)
-
method_missing(:-, other)
-
else
-
result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def since(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:since, other)
-
else
-
utc.since(other).in_time_zone(time_zone)
-
end
-
end
-
-
1
def ago(other)
-
since(-other)
-
end
-
-
1
def advance(options)
-
# If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
-
# otherwise advance from #utc, for accuracy when moving across DST boundaries
-
if options.values_at(:years, :weeks, :months, :days).any?
-
method_missing(:advance, options)
-
else
-
utc.advance(options).in_time_zone(time_zone)
-
end
-
end
-
-
1
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
-
11
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{method_name} # def month
-
time.#{method_name} # time.month
-
end # end
-
EOV
-
end
-
-
1
def usec
-
time.respond_to?(:usec) ? time.usec : 0
-
end
-
-
1
def to_a
-
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
-
end
-
-
1
def to_f
-
utc.to_f
-
end
-
-
1
def to_i
-
utc.to_i
-
end
-
1
alias_method :tv_sec, :to_i
-
-
# A TimeWithZone acts like a Time, so just return +self+.
-
1
def to_time
-
utc
-
end
-
-
1
def to_datetime
-
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
-
end
-
-
# So that +self+ <tt>acts_like?(:time)</tt>.
-
1
def acts_like_time?
-
true
-
end
-
-
# Say we're a Time to thwart type checking.
-
1
def is_a?(klass)
-
klass == ::Time || super
-
end
-
1
alias_method :kind_of?, :is_a?
-
-
1
def freeze
-
period; utc; time # preload instance variables before freezing
-
super
-
end
-
-
1
def marshal_dump
-
[utc, time_zone.name, time]
-
end
-
-
1
def marshal_load(variables)
-
initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
-
end
-
-
# Ensure proxy class responds to all methods that underlying time instance
-
# responds to.
-
1
def respond_to_missing?(sym, include_priv)
-
# consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
-
return false if sym.to_sym == :acts_like_date?
-
time.respond_to?(sym, include_priv)
-
end
-
-
# Send the missing method to +time+ instance, and wrap result in a new
-
# TimeWithZone with the existing +time_zone+.
-
1
def method_missing(sym, *args, &block)
-
wrap_with_time_zone time.__send__(sym, *args, &block)
-
end
-
-
1
private
-
1
def get_period_and_ensure_valid_local_time
-
# we don't want a Time.local instance enforcing its own DST rules as well,
-
# so transfer time values to a utc constructor if necessary
-
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
-
begin
-
@time_zone.period_for_local(@time)
-
rescue ::TZInfo::PeriodNotFound
-
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
-
@time += 1.hour
-
retry
-
end
-
end
-
-
1
def transfer_time_values_to_utc_constructor(time)
-
::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:nsec) ? Rational(time.nsec, 1000) : 0)
-
end
-
-
1
def duration_of_variable_length?(obj)
-
ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
-
end
-
-
1
def wrap_with_time_zone(time)
-
if time.acts_like?(:time)
-
self.class.new(nil, time_zone, time)
-
elsif time.is_a?(Range)
-
wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
-
else
-
time
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActiveSupport
-
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
-
# It allows us to do the following:
-
#
-
# * Limit the set of zones provided by TZInfo to a meaningful subset of 142
-
# zones.
-
# * Retrieve and display zones with a friendlier name
-
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
-
# * Lazily load TZInfo::Timezone instances only when they're needed.
-
# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
-
# +parse+, +at+ and +now+ methods.
-
#
-
# If you set <tt>config.time_zone</tt> in the Rails Application, you can
-
# access this TimeZone object via <tt>Time.zone</tt>:
-
#
-
# # application.rb:
-
# class Application < Rails::Application
-
# config.time_zone = 'Eastern Time (US & Canada)'
-
# end
-
#
-
# Time.zone # => #<TimeZone:0x514834...>
-
# Time.zone.name # => "Eastern Time (US & Canada)"
-
# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
-
#
-
# The version of TZInfo bundled with Active Support only includes the
-
# definitions necessary to support the zones defined by the TimeZone class.
-
# If you need to use zones that aren't defined by TimeZone, you'll need to
-
# install the TZInfo gem (if a recent version of the gem is installed locally,
-
# this will be used instead of the bundled version.)
-
1
class TimeZone
-
# Keys are Rails TimeZone names, values are TZInfo identifiers.
-
1
MAPPING = {
-
"International Date Line West" => "Pacific/Midway",
-
"Midway Island" => "Pacific/Midway",
-
"American Samoa" => "Pacific/Pago_Pago",
-
"Hawaii" => "Pacific/Honolulu",
-
"Alaska" => "America/Juneau",
-
"Pacific Time (US & Canada)" => "America/Los_Angeles",
-
"Tijuana" => "America/Tijuana",
-
"Mountain Time (US & Canada)" => "America/Denver",
-
"Arizona" => "America/Phoenix",
-
"Chihuahua" => "America/Chihuahua",
-
"Mazatlan" => "America/Mazatlan",
-
"Central Time (US & Canada)" => "America/Chicago",
-
"Saskatchewan" => "America/Regina",
-
"Guadalajara" => "America/Mexico_City",
-
"Mexico City" => "America/Mexico_City",
-
"Monterrey" => "America/Monterrey",
-
"Central America" => "America/Guatemala",
-
"Eastern Time (US & Canada)" => "America/New_York",
-
"Indiana (East)" => "America/Indiana/Indianapolis",
-
"Bogota" => "America/Bogota",
-
"Lima" => "America/Lima",
-
"Quito" => "America/Lima",
-
"Atlantic Time (Canada)" => "America/Halifax",
-
"Caracas" => "America/Caracas",
-
"La Paz" => "America/La_Paz",
-
"Santiago" => "America/Santiago",
-
"Newfoundland" => "America/St_Johns",
-
"Brasilia" => "America/Sao_Paulo",
-
"Buenos Aires" => "America/Argentina/Buenos_Aires",
-
"Georgetown" => "America/Guyana",
-
"Greenland" => "America/Godthab",
-
"Mid-Atlantic" => "Atlantic/South_Georgia",
-
"Azores" => "Atlantic/Azores",
-
"Cape Verde Is." => "Atlantic/Cape_Verde",
-
"Dublin" => "Europe/Dublin",
-
"Edinburgh" => "Europe/London",
-
"Lisbon" => "Europe/Lisbon",
-
"London" => "Europe/London",
-
"Casablanca" => "Africa/Casablanca",
-
"Monrovia" => "Africa/Monrovia",
-
"UTC" => "Etc/UTC",
-
"Belgrade" => "Europe/Belgrade",
-
"Bratislava" => "Europe/Bratislava",
-
"Budapest" => "Europe/Budapest",
-
"Ljubljana" => "Europe/Ljubljana",
-
"Prague" => "Europe/Prague",
-
"Sarajevo" => "Europe/Sarajevo",
-
"Skopje" => "Europe/Skopje",
-
"Warsaw" => "Europe/Warsaw",
-
"Zagreb" => "Europe/Zagreb",
-
"Brussels" => "Europe/Brussels",
-
"Copenhagen" => "Europe/Copenhagen",
-
"Madrid" => "Europe/Madrid",
-
"Paris" => "Europe/Paris",
-
"Amsterdam" => "Europe/Amsterdam",
-
"Berlin" => "Europe/Berlin",
-
"Bern" => "Europe/Berlin",
-
"Rome" => "Europe/Rome",
-
"Stockholm" => "Europe/Stockholm",
-
"Vienna" => "Europe/Vienna",
-
"West Central Africa" => "Africa/Algiers",
-
"Bucharest" => "Europe/Bucharest",
-
"Cairo" => "Africa/Cairo",
-
"Helsinki" => "Europe/Helsinki",
-
"Kyiv" => "Europe/Kiev",
-
"Riga" => "Europe/Riga",
-
"Sofia" => "Europe/Sofia",
-
"Tallinn" => "Europe/Tallinn",
-
"Vilnius" => "Europe/Vilnius",
-
"Athens" => "Europe/Athens",
-
"Istanbul" => "Europe/Istanbul",
-
"Minsk" => "Europe/Minsk",
-
"Jerusalem" => "Asia/Jerusalem",
-
"Harare" => "Africa/Harare",
-
"Pretoria" => "Africa/Johannesburg",
-
"Moscow" => "Europe/Moscow",
-
"St. Petersburg" => "Europe/Moscow",
-
"Volgograd" => "Europe/Moscow",
-
"Kuwait" => "Asia/Kuwait",
-
"Riyadh" => "Asia/Riyadh",
-
"Nairobi" => "Africa/Nairobi",
-
"Baghdad" => "Asia/Baghdad",
-
"Tehran" => "Asia/Tehran",
-
"Abu Dhabi" => "Asia/Muscat",
-
"Muscat" => "Asia/Muscat",
-
"Baku" => "Asia/Baku",
-
"Tbilisi" => "Asia/Tbilisi",
-
"Yerevan" => "Asia/Yerevan",
-
"Kabul" => "Asia/Kabul",
-
"Ekaterinburg" => "Asia/Yekaterinburg",
-
"Islamabad" => "Asia/Karachi",
-
"Karachi" => "Asia/Karachi",
-
"Tashkent" => "Asia/Tashkent",
-
"Chennai" => "Asia/Kolkata",
-
"Kolkata" => "Asia/Kolkata",
-
"Mumbai" => "Asia/Kolkata",
-
"New Delhi" => "Asia/Kolkata",
-
"Kathmandu" => "Asia/Kathmandu",
-
"Astana" => "Asia/Dhaka",
-
"Dhaka" => "Asia/Dhaka",
-
"Sri Jayawardenepura" => "Asia/Colombo",
-
"Almaty" => "Asia/Almaty",
-
"Novosibirsk" => "Asia/Novosibirsk",
-
"Rangoon" => "Asia/Rangoon",
-
"Bangkok" => "Asia/Bangkok",
-
"Hanoi" => "Asia/Bangkok",
-
"Jakarta" => "Asia/Jakarta",
-
"Krasnoyarsk" => "Asia/Krasnoyarsk",
-
"Beijing" => "Asia/Shanghai",
-
"Chongqing" => "Asia/Chongqing",
-
"Hong Kong" => "Asia/Hong_Kong",
-
"Urumqi" => "Asia/Urumqi",
-
"Kuala Lumpur" => "Asia/Kuala_Lumpur",
-
"Singapore" => "Asia/Singapore",
-
"Taipei" => "Asia/Taipei",
-
"Perth" => "Australia/Perth",
-
"Irkutsk" => "Asia/Irkutsk",
-
"Ulaan Bataar" => "Asia/Ulaanbaatar",
-
"Seoul" => "Asia/Seoul",
-
"Osaka" => "Asia/Tokyo",
-
"Sapporo" => "Asia/Tokyo",
-
"Tokyo" => "Asia/Tokyo",
-
"Yakutsk" => "Asia/Yakutsk",
-
"Darwin" => "Australia/Darwin",
-
"Adelaide" => "Australia/Adelaide",
-
"Canberra" => "Australia/Melbourne",
-
"Melbourne" => "Australia/Melbourne",
-
"Sydney" => "Australia/Sydney",
-
"Brisbane" => "Australia/Brisbane",
-
"Hobart" => "Australia/Hobart",
-
"Vladivostok" => "Asia/Vladivostok",
-
"Guam" => "Pacific/Guam",
-
"Port Moresby" => "Pacific/Port_Moresby",
-
"Magadan" => "Asia/Magadan",
-
"Solomon Is." => "Pacific/Guadalcanal",
-
"New Caledonia" => "Pacific/Noumea",
-
"Fiji" => "Pacific/Fiji",
-
"Kamchatka" => "Asia/Kamchatka",
-
"Marshall Is." => "Pacific/Majuro",
-
"Auckland" => "Pacific/Auckland",
-
"Wellington" => "Pacific/Auckland",
-
"Nuku'alofa" => "Pacific/Tongatapu",
-
"Tokelau Is." => "Pacific/Fakaofo",
-
"Samoa" => "Pacific/Apia"
-
}
-
-
1
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
-
1
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
-
-
# Assumes self represents an offset from UTC in seconds (as returned from
-
# Time#utc_offset) and turns this into an +HH:MM formatted string.
-
#
-
# TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
-
1
def self.seconds_to_utc_offset(seconds, colon = true)
-
format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
-
sign = (seconds < 0 ? '-' : '+')
-
hours = seconds.abs / 3600
-
minutes = (seconds.abs % 3600) / 60
-
format % [sign, hours, minutes]
-
end
-
-
1
include Comparable
-
1
attr_reader :name
-
1
attr_reader :tzinfo
-
-
# Create a new TimeZone object with the given name and offset. The
-
# offset is the number of seconds that this time zone is offset from UTC
-
# (GMT). Seconds were chosen as the offset unit because that is the unit
-
# that Ruby uses to represent time zone offsets (see Time#utc_offset).
-
1
def initialize(name, utc_offset = nil, tzinfo = nil)
-
self.class.send(:require_tzinfo)
-
-
@name = name
-
@utc_offset = utc_offset
-
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
-
@current_period = nil
-
end
-
-
# Returns the offset of this time zone from UTC in seconds.
-
1
def utc_offset
-
if @utc_offset
-
@utc_offset
-
else
-
@current_period ||= tzinfo.try(:current_period)
-
@current_period.try(:utc_offset)
-
end
-
end
-
-
# Returns the offset of this time zone as a formatted string, of the
-
# format "+HH:MM".
-
1
def formatted_offset(colon=true, alternate_utc_string = nil)
-
utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Compare this time zone to the parameter. The two are compared first on
-
# their offsets, and then by name.
-
1
def <=>(zone)
-
result = (utc_offset <=> zone.utc_offset)
-
result = (name <=> zone.name) if result == 0
-
result
-
end
-
-
# Compare #name and TZInfo identifier to a supplied regexp, returning +true+
-
# if a match is found.
-
1
def =~(re)
-
return true if name =~ re || MAPPING[name] =~ re
-
end
-
-
# Returns a textual representation of this time zone.
-
1
def to_s
-
"(GMT#{formatted_offset}) #{name}"
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from given values.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
-
1
def local(*args)
-
time = Time.utc_time(*args)
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from number of seconds since the Unix epoch.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.utc(2000).to_f # => 946684800.0
-
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
1
def at(secs)
-
Time.at(secs).utc.in_time_zone(self)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from parsed string.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# If upper components are missing from the string, they are supplied from
-
# TimeZone#now:
-
#
-
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
-
1
def parse(str, now=now)
-
date_parts = Date._parse(str)
-
return if date_parts.empty?
-
time = Time.parse(str, now) rescue DateTime.parse(str)
-
-
if date_parts[:offset].nil?
-
if date_parts[:hour] && time.hour != date_parts[:hour]
-
time = DateTime.parse(str)
-
end
-
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
else
-
time.in_time_zone(self)
-
end
-
end
-
-
# Returns an ActiveSupport::TimeWithZone instance representing the current
-
# time in the time zone represented by +self+.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
-
1
def now
-
time_now.utc.in_time_zone(self)
-
end
-
-
# Return the current date in this time zone.
-
1
def today
-
tzinfo.now.to_date
-
end
-
-
# Adjust the given time to the simultaneous time in the time zone
-
# represented by +self+. Returns a Time.utc() instance -- if you want an
-
# ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
-
1
def utc_to_local(time)
-
tzinfo.utc_to_local(time)
-
end
-
-
# Adjust the given time to the simultaneous time in UTC. Returns a
-
# Time.utc() instance.
-
1
def local_to_utc(time, dst=true)
-
tzinfo.local_to_utc(time, dst)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_utc(time)
-
tzinfo.period_for_utc(time)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_local(time, dst=true)
-
tzinfo.period_for_local(time, dst)
-
end
-
-
1
def self.find_tzinfo(name)
-
TZInfo::TimezoneProxy.new(MAPPING[name] || name)
-
end
-
-
1
class << self
-
1
alias_method :create, :new
-
-
# Return a TimeZone instance with the given name, or +nil+ if no
-
# such TimeZone instance exists. (This exists to support the use of
-
# this class with the +composed_of+ macro.)
-
1
def new(name)
-
self[name]
-
end
-
-
# Return an array of all TimeZone objects. There are multiple
-
# TimeZone objects per time zone, in many cases, to make it easier
-
# for users to find their own time zone.
-
1
def all
-
@zones ||= zones_map.values.sort
-
end
-
-
1
def zones_map
-
@zones_map ||= begin
-
new_zones_names = MAPPING.keys - lazy_zones_map.keys
-
new_zones = Hash[new_zones_names.map { |place| [place, create(place)] }]
-
-
lazy_zones_map.merge(new_zones)
-
end
-
end
-
-
# Locate a specific time zone object. If the argument is a string, it
-
# is interpreted to mean the name of the timezone to locate. If it is a
-
# numeric value it is either the hour offset, or the second offset, of the
-
# timezone to find. (The first one with that offset will be returned.)
-
# Returns +nil+ if no such time zone is known to the system.
-
1
def [](arg)
-
case arg
-
when String
-
begin
-
lazy_zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
-
rescue TZInfo::InvalidTimezoneIdentifier
-
nil
-
end
-
when Numeric, ActiveSupport::Duration
-
arg *= 3600 if arg.abs <= 13
-
all.find { |z| z.utc_offset == arg.to_i }
-
else
-
raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
-
end
-
end
-
-
# A convenience method for returning a collection of TimeZone objects
-
# for time zones in the USA.
-
1
def us_zones
-
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
-
end
-
-
1
protected
-
-
1
def require_tzinfo
-
require 'tzinfo' unless defined?(::TZInfo)
-
rescue LoadError
-
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
-
raise
-
end
-
-
1
private
-
-
1
def lookup(name)
-
(tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze)
-
end
-
-
1
def lazy_zones_map
-
require_tzinfo
-
-
@lazy_zones_map ||= Hash.new do |hash, place|
-
hash[place] = create(place) if MAPPING.has_key?(place)
-
end
-
end
-
end
-
-
1
private
-
-
1
def time_now
-
Time.now
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module VERSION #:nodoc:
-
1
MAJOR = 4
-
1
MINOR = 0
-
1
TINY = 0
-
1
PRE = "beta"
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
1
require 'time'
-
1
require 'base64'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveSupport
-
# = XmlMini
-
#
-
# To use the much faster libxml parser:
-
# gem 'libxml-ruby', '=0.9.7'
-
# XmlMini.backend = 'LibXML'
-
1
module XmlMini
-
1
extend self
-
-
# This module decorates files deserialized using Hash.from_xml with
-
# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
-
1
module FileLike #:nodoc:
-
1
attr_writer :original_filename, :content_type
-
-
1
def original_filename
-
@original_filename || 'untitled'
-
end
-
-
1
def content_type
-
@content_type || 'application/octet-stream'
-
end
-
end
-
-
DEFAULT_ENCODINGS = {
-
"binary" => "base64"
-
1
} unless defined?(DEFAULT_ENCODINGS)
-
-
TYPE_NAMES = {
-
"Symbol" => "symbol",
-
"Fixnum" => "integer",
-
"Bignum" => "integer",
-
"BigDecimal" => "decimal",
-
"Float" => "float",
-
"TrueClass" => "boolean",
-
"FalseClass" => "boolean",
-
"Date" => "date",
-
"DateTime" => "dateTime",
-
"Time" => "dateTime",
-
"Array" => "array",
-
"Hash" => "hash"
-
1
} unless defined?(TYPE_NAMES)
-
-
FORMATTING = {
-
1
"symbol" => Proc.new { |symbol| symbol.to_s },
-
"date" => Proc.new { |date| date.to_s(:db) },
-
32
"dateTime" => Proc.new { |time| time.xmlschema },
-
"binary" => Proc.new { |binary| ::Base64.encode64(binary) },
-
32
"yaml" => Proc.new { |yaml| yaml.to_yaml }
-
1
} unless defined?(FORMATTING)
-
-
# TODO use regexp instead of Date.parse
-
1
unless defined?(PARSING)
-
1
PARSING = {
-
"symbol" => Proc.new { |symbol| symbol.to_sym },
-
"date" => Proc.new { |date| ::Date.parse(date) },
-
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
-
"integer" => Proc.new { |integer| integer.to_i },
-
"float" => Proc.new { |float| float.to_f },
-
"decimal" => Proc.new { |number| BigDecimal(number) },
-
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
-
"string" => Proc.new { |string| string.to_s },
-
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
-
"base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
-
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
-
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
-
}
-
-
1
PARSING.update(
-
"double" => PARSING["float"],
-
"dateTime" => PARSING["datetime"]
-
)
-
end
-
-
1
attr_reader :backend
-
1
delegate :parse, :to => :backend
-
-
1
def backend=(name)
-
1
if name.is_a?(Module)
-
@backend = name
-
else
-
1
require "active_support/xml_mini/#{name.downcase}"
-
1
@backend = ActiveSupport.const_get("XmlMini_#{name}")
-
end
-
end
-
-
1
def with_backend(name)
-
old_backend, self.backend = backend, name
-
yield
-
ensure
-
self.backend = old_backend
-
end
-
-
1
def to_tag(key, value, options)
-
221
type_name = options.delete(:type)
-
221
merged_options = options.merge(:root => key, :skip_instruct => true)
-
-
221
if value.is_a?(::Method) || value.is_a?(::Proc)
-
if value.arity == 1
-
value.call(merged_options)
-
else
-
value.call(merged_options, key.to_s.singularize)
-
end
-
221
elsif value.respond_to?(:to_xml)
-
2
value.to_xml(merged_options)
-
else
-
219
type_name ||= TYPE_NAMES[value.class.name]
-
219
type_name ||= value.class.name if value && !value.respond_to?(:to_str)
-
219
type_name = type_name.to_s if type_name
-
219
type_name = "dateTime" if type_name == "datetime"
-
-
219
key = rename_key(key.to_s, options)
-
-
219
attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name }
-
219
attributes[:nil] = true if value.nil?
-
-
219
encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
-
219
attributes[:encoding] = encoding if encoding
-
-
219
formatted_value = FORMATTING[type_name] && !value.nil? ?
-
FORMATTING[type_name].call(value) : value
-
-
219
options[:builder].tag!(key, formatted_value, attributes)
-
end
-
end
-
-
1
def rename_key(key, options = {})
-
494
camelize = options[:camelize]
-
494
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
-
494
if camelize
-
55
key = true == camelize ? key.camelize : key.camelize(camelize)
-
end
-
494
key = _dasherize(key) if dasherize
-
494
key
-
end
-
-
1
protected
-
-
1
def _dasherize(key)
-
# $2 must be a non-greedy regex for this to work
-
450
left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
-
450
"#{left}#{middle.tr('_ ', '--')}#{right}"
-
end
-
-
# TODO: Add support for other encodings
-
1
def _parse_binary(bin, entity) #:nodoc:
-
case entity['encoding']
-
when 'base64'
-
::Base64.decode64(bin)
-
else
-
bin
-
end
-
end
-
-
1
def _parse_file(file, entity)
-
f = StringIO.new(::Base64.decode64(file))
-
f.extend(FileLike)
-
f.original_filename = entity['name']
-
f.content_type = entity['content_type']
-
f
-
end
-
end
-
-
1
XmlMini.backend = 'REXML'
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'stringio'
-
-
1
module ActiveSupport
-
1
module XmlMini_REXML #:nodoc:
-
1
extend self
-
-
1
CONTENT_KEY = '__content__'.freeze
-
-
# Parse an XML Document string or IO into a simple hash.
-
#
-
# Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
-
# and uses the defaults from Active Support.
-
#
-
# data::
-
# XML Document string or IO to parse
-
1
def parse(data)
-
if !data.respond_to?(:read)
-
data = StringIO.new(data || '')
-
end
-
-
char = data.getc
-
if char.nil?
-
{}
-
else
-
data.ungetc(char)
-
silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
-
doc = REXML::Document.new(data)
-
-
if doc.root
-
merge_element!({}, doc.root)
-
else
-
raise REXML::ParseException,
-
"The document #{doc.to_s.inspect} does not have a valid root"
-
end
-
end
-
end
-
-
1
private
-
# Convert an XML element and merge into the hash
-
#
-
# hash::
-
# Hash to merge the converted element into.
-
# element::
-
# XML element to merge into hash
-
1
def merge_element!(hash, element)
-
merge!(hash, element.name, collapse(element))
-
end
-
-
# Actually converts an XML document element into a data structure.
-
#
-
# element::
-
# The document element to be collapsed.
-
1
def collapse(element)
-
hash = get_attributes(element)
-
-
if element.has_elements?
-
element.each_element {|child| merge_element!(hash, child) }
-
merge_texts!(hash, element) unless empty_content?(element)
-
hash
-
else
-
merge_texts!(hash, element)
-
end
-
end
-
-
# Merge all the texts of an element into the hash
-
#
-
# hash::
-
# Hash to add the converted element to.
-
# element::
-
# XML element whose texts are to me merged into the hash
-
1
def merge_texts!(hash, element)
-
unless element.has_text?
-
hash
-
else
-
# must use value to prevent double-escaping
-
texts = ''
-
element.texts.each { |t| texts << t.value }
-
merge!(hash, CONTENT_KEY, texts)
-
end
-
end
-
-
# Adds a new key/value pair to an existing Hash. If the key to be added
-
# already exists and the existing value associated with key is not
-
# an Array, it will be wrapped in an Array. Then the new value is
-
# appended to that Array.
-
#
-
# hash::
-
# Hash to add key/value pair to.
-
# key::
-
# Key to be added.
-
# value::
-
# Value to be associated with key.
-
1
def merge!(hash, key, value)
-
if hash.has_key?(key)
-
if hash[key].instance_of?(Array)
-
hash[key] << value
-
else
-
hash[key] = [hash[key], value]
-
end
-
elsif value.instance_of?(Array)
-
hash[key] = [value]
-
else
-
hash[key] = value
-
end
-
hash
-
end
-
-
# Converts the attributes array of an XML element into a hash.
-
# Returns an empty Hash if node has no attributes.
-
#
-
# element::
-
# XML element to extract attributes from.
-
1
def get_attributes(element)
-
attributes = {}
-
element.attributes.each { |n,v| attributes[n] = v }
-
attributes
-
end
-
-
# Determines if a document element has text content
-
#
-
# element::
-
# XML element to be checked.
-
1
def empty_content?(element)
-
element.texts.join.blank?
-
end
-
end
-
end
-
# bust gem prelude
-
1
require 'bundler'
-
1
Bundler.setup
-
# A wrapper for OpenBSD's bcrypt/crypt_blowfish password-hashing algorithm.
-
-
1
if RUBY_PLATFORM == "java"
-
require 'java'
-
else
-
1
require "openssl"
-
end
-
-
1
if defined?(RUBY_ENGINE) and RUBY_ENGINE == "maglev"
-
require 'bcrypt_engine'
-
else
-
1
require 'bcrypt_ext'
-
end
-
-
# A Ruby library implementing OpenBSD's bcrypt()/crypt_blowfish algorithm for
-
# hashing passwords.
-
1
module BCrypt
-
1
module Errors
-
1
class InvalidSalt < StandardError; end # The salt parameter provided to bcrypt() is invalid.
-
1
class InvalidHash < StandardError; end # The hash parameter provided to bcrypt() is invalid.
-
1
class InvalidCost < StandardError; end # The cost parameter provided to bcrypt() is invalid.
-
1
class InvalidSecret < StandardError; end # The secret parameter provided to bcrypt() is invalid.
-
end
-
-
# A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
-
1
class Engine
-
# The default computational expense parameter.
-
1
DEFAULT_COST = 10
-
# The minimum cost supported by the algorithm.
-
1
MIN_COST = 4
-
# Maximum possible size of bcrypt() salts.
-
1
MAX_SALT_LENGTH = 16
-
-
1
if RUBY_PLATFORM != "java"
-
# C-level routines which, if they don't get the right input, will crash the
-
# hell out of the Ruby process.
-
1
private_class_method :__bc_salt
-
1
private_class_method :__bc_crypt
-
end
-
-
# Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates
-
# a bcrypt() password hash.
-
1
def self.hash_secret(secret, salt, cost = nil)
-
7
if valid_secret?(secret)
-
7
if valid_salt?(salt)
-
7
if cost.nil?
-
2
cost = autodetect_cost(salt)
-
end
-
-
7
if RUBY_PLATFORM == "java"
-
Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s)
-
else
-
7
__bc_crypt(secret.to_s, salt)
-
end
-
else
-
raise Errors::InvalidSalt.new("invalid salt")
-
end
-
else
-
raise Errors::InvalidSecret.new("invalid secret")
-
end
-
end
-
-
# Generates a random salt with a given computational cost.
-
1
def self.generate_salt(cost = DEFAULT_COST)
-
5
cost = cost.to_i
-
5
if cost > 0
-
5
if cost < MIN_COST
-
cost = MIN_COST
-
end
-
5
if RUBY_PLATFORM == "java"
-
Java.bcrypt_jruby.BCrypt.gensalt(cost)
-
else
-
5
prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
-
5
__bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH))
-
end
-
else
-
raise Errors::InvalidCost.new("cost must be numeric and > 0")
-
end
-
end
-
-
# Returns true if +salt+ is a valid bcrypt() salt, false if not.
-
1
def self.valid_salt?(salt)
-
7
!!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
-
end
-
-
# Returns true if +secret+ is a valid bcrypt() secret, false if not.
-
1
def self.valid_secret?(secret)
-
7
secret.respond_to?(:to_s)
-
end
-
-
# Returns the cost factor which will result in computation times less than +upper_time_limit_in_ms+.
-
#
-
# Example:
-
#
-
# BCrypt.calibrate(200) #=> 10
-
# BCrypt.calibrate(1000) #=> 12
-
#
-
# # should take less than 200ms
-
# BCrypt::Password.create("woo", :cost => 10)
-
#
-
# # should take less than 1000ms
-
# BCrypt::Password.create("woo", :cost => 12)
-
1
def self.calibrate(upper_time_limit_in_ms)
-
40.times do |i|
-
start_time = Time.now
-
Password.create("testing testing", :cost => i+1)
-
end_time = Time.now - start_time
-
return i if end_time * 1_000 > upper_time_limit_in_ms
-
end
-
end
-
-
# Autodetects the cost from the salt string.
-
1
def self.autodetect_cost(salt)
-
2
salt[4..5].to_i
-
end
-
end
-
-
# A password management class which allows you to safely store users' passwords and compare them.
-
#
-
# Example usage:
-
#
-
# include BCrypt
-
#
-
# # hash a user's password
-
# @password = Password.create("my grand secret")
-
# @password #=> "$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG"
-
#
-
# # store it safely
-
# @user.update_attribute(:password, @password)
-
#
-
# # read it back
-
# @user.reload!
-
# @db_password = Password.new(@user.password)
-
#
-
# # compare it after retrieval
-
# @db_password == "my grand secret" #=> true
-
# @db_password == "a paltry guess" #=> false
-
#
-
1
class Password < String
-
# The hash portion of the stored password hash.
-
1
attr_reader :checksum
-
# The salt of the store password hash (including version and cost).
-
1
attr_reader :salt
-
# The version of the bcrypt() algorithm used to create the hash.
-
1
attr_reader :version
-
# The cost factor used to create the hash.
-
1
attr_reader :cost
-
-
1
class << self
-
# Hashes a secret, returning a BCrypt::Password instance. Takes an optional <tt>:cost</tt> option, which is a
-
# logarithmic variable which determines how computational expensive the hash is to calculate (a <tt>:cost</tt> of
-
# 4 is twice as much work as a <tt>:cost</tt> of 3). The higher the <tt>:cost</tt> the harder it becomes for
-
# attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check
-
# users' passwords.
-
#
-
# Example:
-
#
-
# @password = BCrypt::Password.create("my secret", :cost => 13)
-
1
def create(secret, options = { :cost => BCrypt::Engine::DEFAULT_COST })
-
5
raise ArgumentError if options[:cost] > 31
-
5
Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(options[:cost]), options[:cost]))
-
end
-
end
-
-
# Initializes a BCrypt::Password instance with the data from a stored hash.
-
1
def initialize(raw_hash)
-
7
if valid_hash?(raw_hash)
-
7
self.replace(raw_hash)
-
7
@version, @cost, @salt, @checksum = split_hash(self)
-
else
-
raise Errors::InvalidHash.new("invalid hash")
-
end
-
end
-
-
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
-
1
def ==(secret)
-
2
super(BCrypt::Engine.hash_secret(secret, @salt))
-
end
-
1
alias_method :is_password?, :==
-
-
1
private
-
# Returns true if +h+ is a valid hash.
-
1
def valid_hash?(h)
-
7
h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
-
end
-
-
# call-seq:
-
# split_hash(raw_hash) -> version, cost, salt, hash
-
#
-
# Splits +h+ into version, cost, salt, and hash and returns them in that order.
-
1
def split_hash(h)
-
7
_, v, c, mash = h.split('$')
-
7
return v, c.to_i, h[0, 29].to_str, mash[-31, 31].to_str
-
end
-
end
-
end
-
#!/usr/bin/env ruby
-
-
#--
-
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
-
# All rights reserved.
-
-
# Permission is granted for use, copying, modification, distribution,
-
# and distribution of modified versions of this work as long as the
-
# above copyright notice is included.
-
#++
-
-
1
require 'builder/xmlmarkup'
-
1
require 'builder/xmlevents'
-
#!/usr/bin/env ruby
-
#--
-
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
-
# All rights reserved.
-
-
# Permission is granted for use, copying, modification, distribution,
-
# and distribution of modified versions of this work as long as the
-
# above copyright notice is included.
-
#++
-
-
######################################################################
-
# BlankSlate has been promoted to a top level name and is now
-
# available as a standalone gem. We make the name available in the
-
# Builder namespace for compatibility.
-
#
-
1
module Builder
-
1
if Object::const_defined?(:BasicObject)
-
1
BlankSlate = ::BasicObject
-
else
-
require 'blankslate'
-
BlankSlate = ::BlankSlate
-
end
-
end
-
#!/usr/bin/env ruby
-
-
# The XChar library is provided courtesy of Sam Ruby (See
-
# http://intertwingly.net/stories/2005/09/28/xchar.rb)
-
-
# --------------------------------------------------------------------
-
-
# If the Builder::XChar module is not currently defined, fail on any
-
# name clashes in standard library classes.
-
-
1
module Builder
-
1
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
-
if klass.method_defined?(method_name.to_s)
-
fail RuntimeError,
-
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
-
end
-
end
-
end
-
-
1
if ! defined?(Builder::XChar) and ! String.method_defined?(:encode)
-
Builder.check_for_name_collision(String, "to_xs")
-
Builder.check_for_name_collision(Fixnum, "xchr")
-
end
-
-
######################################################################
-
1
module Builder
-
-
####################################################################
-
# XML Character converter, from Sam Ruby:
-
# (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
-
#
-
1
module XChar # :nodoc:
-
-
# See
-
# http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
-
# for details.
-
1
CP1252 = { # :nodoc:
-
128 => 8364, # euro sign
-
130 => 8218, # single low-9 quotation mark
-
131 => 402, # latin small letter f with hook
-
132 => 8222, # double low-9 quotation mark
-
133 => 8230, # horizontal ellipsis
-
134 => 8224, # dagger
-
135 => 8225, # double dagger
-
136 => 710, # modifier letter circumflex accent
-
137 => 8240, # per mille sign
-
138 => 352, # latin capital letter s with caron
-
139 => 8249, # single left-pointing angle quotation mark
-
140 => 338, # latin capital ligature oe
-
142 => 381, # latin capital letter z with caron
-
145 => 8216, # left single quotation mark
-
146 => 8217, # right single quotation mark
-
147 => 8220, # left double quotation mark
-
148 => 8221, # right double quotation mark
-
149 => 8226, # bullet
-
150 => 8211, # en dash
-
151 => 8212, # em dash
-
152 => 732, # small tilde
-
153 => 8482, # trade mark sign
-
154 => 353, # latin small letter s with caron
-
155 => 8250, # single right-pointing angle quotation mark
-
156 => 339, # latin small ligature oe
-
158 => 382, # latin small letter z with caron
-
159 => 376, # latin capital letter y with diaeresis
-
}
-
-
# See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
-
1
PREDEFINED = {
-
38 => '&', # ampersand
-
60 => '<', # left angle bracket
-
62 => '>', # right angle bracket
-
}
-
-
# See http://www.w3.org/TR/REC-xml/#charsets for details.
-
1
VALID = [
-
0x9, 0xA, 0xD,
-
(0x20..0xD7FF),
-
(0xE000..0xFFFD),
-
(0x10000..0x10FFFF)
-
]
-
-
# http://www.fileformat.info/info/unicode/char/fffd/index.htm
-
1
REPLACEMENT_CHAR =
-
if String.method_defined?(:encode)
-
1
"\uFFFD"
-
elsif $KCODE == 'UTF8'
-
"\xEF\xBF\xBD"
-
else
-
'*'
-
end
-
end
-
-
end
-
-
-
1
if String.method_defined?(:encode)
-
1
module Builder
-
1
module XChar # :nodoc:
-
1
CP1252_DIFFERENCES, UNICODE_EQUIVALENT = Builder::XChar::CP1252.each.
-
inject([[],[]]) {|(domain,range),(key,value)|
-
27
[domain << key,range << value]
-
2
}.map {|seq| seq.pack('U*').force_encoding('utf-8')}
-
-
1
XML_PREDEFINED = Regexp.new('[' +
-
Builder::XChar::PREDEFINED.keys.pack('U*').force_encoding('utf-8') +
-
']')
-
-
1
INVALID_XML_CHAR = Regexp.new('[^'+
-
Builder::XChar::VALID.map { |item|
-
6
case item
-
when Fixnum
-
3
[item].pack('U').force_encoding('utf-8')
-
when Range
-
3
[item.first, '-'.ord, item.last].pack('UUU').force_encoding('utf-8')
-
end
-
}.join +
-
']')
-
-
1
ENCODING_BINARY = Encoding.find('BINARY')
-
1
ENCODING_UTF8 = Encoding.find('UTF-8')
-
1
ENCODING_ISO1 = Encoding.find('ISO-8859-1')
-
-
# convert a string to valid UTF-8, compensating for a number of
-
# common errors.
-
1
def XChar.unicode(string)
-
726
if string.encoding == ENCODING_BINARY
-
if string.ascii_only?
-
string
-
else
-
string = string.clone.force_encoding(ENCODING_UTF8)
-
if string.valid_encoding?
-
string
-
else
-
string.encode(ENCODING_UTF8, ENCODING_ISO1)
-
end
-
end
-
-
726
elsif string.encoding == ENCODING_UTF8
-
if string.valid_encoding?
-
string
-
else
-
string.encode(ENCODING_UTF8, ENCODING_ISO1)
-
end
-
-
else
-
726
string.encode(ENCODING_UTF8)
-
end
-
end
-
-
# encode a string per XML rules
-
1
def XChar.encode(string)
-
726
unicode(string).
-
tr(CP1252_DIFFERENCES, UNICODE_EQUIVALENT).
-
gsub(INVALID_XML_CHAR, REPLACEMENT_CHAR).
-
gsub(XML_PREDEFINED) {|c| PREDEFINED[c.ord]}
-
end
-
end
-
end
-
-
else
-
-
######################################################################
-
# Enhance the Fixnum class with a XML escaped character conversion.
-
#
-
class Fixnum
-
XChar = Builder::XChar if ! defined?(XChar)
-
-
# XML escaped version of chr. When <tt>escape</tt> is set to false
-
# the CP1252 fix is still applied but utf-8 characters are not
-
# converted to character entities.
-
def xchr(escape=true)
-
n = XChar::CP1252[self] || self
-
case n when *XChar::VALID
-
XChar::PREDEFINED[n] or
-
(n<128 ? n.chr : (escape ? "&##{n};" : [n].pack('U*')))
-
else
-
Builder::XChar::REPLACEMENT_CHAR
-
end
-
end
-
end
-
-
-
######################################################################
-
# Enhance the String class with a XML escaped character version of
-
# to_s.
-
#
-
class String
-
# XML escaped version of to_s. When <tt>escape</tt> is set to false
-
# the CP1252 fix is still applied but utf-8 characters are not
-
# converted to character entities.
-
def to_xs(escape=true)
-
unpack('U*').map {|n| n.xchr(escape)}.join # ASCII, UTF-8
-
rescue
-
unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
-
end
-
end
-
end
-
#!/usr/bin/env ruby
-
-
1
require 'builder/blankslate'
-
-
1
module Builder
-
-
# Generic error for builder
-
1
class IllegalBlockError < RuntimeError; end
-
-
# XmlBase is a base class for building XML builders. See
-
# Builder::XmlMarkup and Builder::XmlEvents for examples.
-
1
class XmlBase < BlankSlate
-
-
1
class << self
-
1
attr_accessor :cache_method_calls
-
end
-
-
# Create an XML markup builder.
-
#
-
# out:: Object receiving the markup. +out+ must respond to
-
# <tt><<</tt>.
-
# indent:: Number of spaces used for indentation (0 implies no
-
# indentation and no line breaks).
-
# initial:: Level of initial indentation.
-
# encoding:: When <tt>encoding</tt> and $KCODE are set to 'utf-8'
-
# characters aren't converted to character entities in
-
# the output stream.
-
1
def initialize(indent=0, initial=0, encoding='utf-8')
-
36
@indent = indent
-
36
@level = initial
-
36
@encoding = encoding.downcase
-
end
-
-
# Create a tag named +sym+. Other than the first argument which
-
# is the tag name, the arguments are the same as the tags
-
# implemented via <tt>method_missing</tt>.
-
1
def tag!(sym, *args, &block)
-
281
text = nil
-
281
attrs = nil
-
281
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
-
281
sym = sym.to_sym unless sym.class == ::Symbol
-
281
args.each do |arg|
-
455
case arg
-
when ::Hash
-
233
attrs ||= {}
-
233
attrs.merge!(arg)
-
when nil
-
# do nothing
-
else
-
221
text ||= ''
-
221
text << arg.to_s
-
end
-
end
-
281
if block
-
59
unless text.nil?
-
::Kernel::raise ::ArgumentError,
-
"XmlMarkup cannot mix a text argument with a block"
-
end
-
59
_indent
-
59
_start_tag(sym, attrs)
-
59
_newline
-
59
begin
-
59
_nested_structures(block)
-
ensure
-
59
_indent
-
59
_end_tag(sym)
-
59
_newline
-
end
-
elsif text.nil?
-
1
_indent
-
1
_start_tag(sym, attrs, true)
-
1
_newline
-
else
-
221
_indent
-
221
_start_tag(sym, attrs)
-
221
text! text
-
221
_end_tag(sym)
-
221
_newline
-
end
-
281
@target
-
end
-
-
# Create XML markup based on the name of the method. This method
-
# is never invoked directly, but is called for each markup method
-
# in the markup block that isn't cached.
-
1
def method_missing(sym, *args, &block)
-
1
cache_method_call(sym) if ::Builder::XmlBase.cache_method_calls
-
1
tag!(sym, *args, &block)
-
end
-
-
# Append text to the output target. Escape any markup. May be
-
# used within the markup brackets as:
-
#
-
# builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
-
1
def text!(text)
-
517
_text(_escape(text))
-
end
-
-
# Append text to the output target without escaping any markup.
-
# May be used within the markup brackets as:
-
#
-
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
-
#
-
# This is useful when using non-builder enabled software that
-
# generates strings. Just insert the string directly into the
-
# builder without changing the inserted markup.
-
#
-
# It is also useful for stacking builder objects. Builders only
-
# use <tt><<</tt> to append to the target, so by supporting this
-
# method/operation builders can use other builders as their
-
# targets.
-
1
def <<(text)
-
_text(text)
-
end
-
-
# For some reason, nil? is sent to the XmlMarkup object. If nil?
-
# is not defined and method_missing is invoked, some strange kind
-
# of recursion happens. Since nil? won't ever be an XML tag, it
-
# is pretty safe to define it here. (Note: this is an example of
-
# cargo cult programming,
-
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
-
1
def nil?
-
false
-
end
-
-
1
private
-
-
1
require 'builder/xchar'
-
1
if ::String.method_defined?(:encode)
-
1
def _escape(text)
-
726
result = XChar.encode(text)
-
726
begin
-
726
encoding = ::Encoding::find(@encoding)
-
726
raise Exception if encoding.dummy?
-
726
result.encode(encoding)
-
rescue
-
# if the encoding can't be supported, use numeric character references
-
result.
-
gsub(/[^\u0000-\u007F]/) {|c| "&##{c.ord};"}.
-
force_encoding('ascii')
-
end
-
end
-
else
-
def _escape(text)
-
if (text.method(:to_xs).arity == 0)
-
text.to_xs
-
else
-
text.to_xs((@encoding != 'utf-8' or $KCODE != 'UTF8'))
-
end
-
end
-
end
-
-
1
def _escape_attribute(text)
-
209
_escape(text).gsub("\n", " ").gsub("\r", " ").
-
gsub(%r{"}, '"') # " WART
-
end
-
-
1
def _newline
-
372
return if @indent == 0
-
181
text! "\n"
-
end
-
-
1
def _indent
-
372
return if @indent == 0 || @level == 0
-
115
text!(" " * (@level * @indent))
-
end
-
-
1
def _nested_structures(block)
-
59
@level += 1
-
59
block.call(self)
-
ensure
-
59
@level -= 1
-
end
-
-
# If XmlBase.cache_method_calls = true, we dynamicly create the method
-
# missed as an instance method on the XMLBase object. Because XML
-
# documents are usually very repetative in nature, the next node will
-
# be handled by the new method instead of method_missing. As
-
# method_missing is very slow, this speeds up document generation
-
# significantly.
-
1
def cache_method_call(sym)
-
2
class << self; self; end.class_eval do
-
1
define_method(sym) do |*args, &block|
-
tag!(sym, *args, &block)
-
end
-
end
-
end
-
end
-
-
1
XmlBase.cache_method_calls = true
-
-
end
-
#!/usr/bin/env ruby
-
-
#--
-
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
-
# All rights reserved.
-
-
# Permission is granted for use, copying, modification, distribution,
-
# and distribution of modified versions of this work as long as the
-
# above copyright notice is included.
-
#++
-
-
1
require 'builder/xmlmarkup'
-
-
1
module Builder
-
-
# Create a series of SAX-like XML events (e.g. start_tag, end_tag)
-
# from the markup code. XmlEvent objects are used in a way similar
-
# to XmlMarkup objects, except that a series of events are generated
-
# and passed to a handler rather than generating character-based
-
# markup.
-
#
-
# Usage:
-
# xe = Builder::XmlEvents.new(hander)
-
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
-
#
-
# Indentation may also be selected by providing value for the
-
# indentation size and initial indentation level.
-
#
-
# xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
-
#
-
# == XML Event Handler
-
#
-
# The handler object must expect the following events.
-
#
-
# [<tt>start_tag(tag, attrs)</tt>]
-
# Announces that a new tag has been found. +tag+ is the name of
-
# the tag and +attrs+ is a hash of attributes for the tag.
-
#
-
# [<tt>end_tag(tag)</tt>]
-
# Announces that an end tag for +tag+ has been found.
-
#
-
# [<tt>text(text)</tt>]
-
# Announces that a string of characters (+text+) has been found.
-
# A series of characters may be broken up into more than one
-
# +text+ call, so the client cannot assume that a single
-
# callback contains all the text data.
-
#
-
1
class XmlEvents < XmlMarkup
-
1
def text!(text)
-
@target.text(text)
-
end
-
-
1
def _start_tag(sym, attrs, end_too=false)
-
@target.start_tag(sym, attrs)
-
_end_tag(sym) if end_too
-
end
-
-
1
def _end_tag(sym)
-
@target.end_tag(sym)
-
end
-
end
-
-
end
-
#!/usr/bin/env ruby
-
#--
-
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
-
# All rights reserved.
-
-
# Permission is granted for use, copying, modification, distribution,
-
# and distribution of modified versions of this work as long as the
-
# above copyright notice is included.
-
#++
-
-
# Provide a flexible and easy to use Builder for creating XML markup.
-
# See XmlBuilder for usage details.
-
-
1
require 'builder/xmlbase'
-
-
1
module Builder
-
-
# Create XML markup easily. All (well, almost all) methods sent to
-
# an XmlMarkup object will be translated to the equivalent XML
-
# markup. Any method with a block will be treated as an XML markup
-
# tag with nested markup in the block.
-
#
-
# Examples will demonstrate this easier than words. In the
-
# following, +xm+ is an +XmlMarkup+ object.
-
#
-
# xm.em("emphasized") # => <em>emphasized</em>
-
# xm.em { xm.b("emp & bold") } # => <em><b>emph & bold</b></em>
-
# xm.a("A Link", "href"=>"http://onestepback.org")
-
# # => <a href="http://onestepback.org">A Link</a>
-
# xm.div { xm.br } # => <div><br/></div>
-
# xm.target("name"=>"compile", "option"=>"fast")
-
# # => <target option="fast" name="compile"\>
-
# # NOTE: order of attributes is not specified.
-
#
-
# xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
-
# xm.html { # <html>
-
# xm.head { # <head>
-
# xm.title("History") # <title>History</title>
-
# } # </head>
-
# xm.body { # <body>
-
# xm.comment! "HI" # <!-- HI -->
-
# xm.h1("Header") # <h1>Header</h1>
-
# xm.p("paragraph") # <p>paragraph</p>
-
# } # </body>
-
# } # </html>
-
#
-
# == Notes:
-
#
-
# * The order that attributes are inserted in markup tags is
-
# undefined.
-
#
-
# * Sometimes you wish to insert text without enclosing tags. Use
-
# the <tt>text!</tt> method to accomplish this.
-
#
-
# Example:
-
#
-
# xm.div { # <div>
-
# xm.text! "line"; xm.br # line<br/>
-
# xm.text! "another line"; xmbr # another line<br/>
-
# } # </div>
-
#
-
# * The special XML characters <, >, and & are converted to <,
-
# > and & automatically. Use the <tt><<</tt> operation to
-
# insert text without modification.
-
#
-
# * Sometimes tags use special characters not allowed in ruby
-
# identifiers. Use the <tt>tag!</tt> method to handle these
-
# cases.
-
#
-
# Example:
-
#
-
# xml.tag!("SOAP:Envelope") { ... }
-
#
-
# will produce ...
-
#
-
# <SOAP:Envelope> ... </SOAP:Envelope>"
-
#
-
# <tt>tag!</tt> will also take text and attribute arguments (after
-
# the tag name) like normal markup methods. (But see the next
-
# bullet item for a better way to handle XML namespaces).
-
#
-
# * Direct support for XML namespaces is now available. If the
-
# first argument to a tag call is a symbol, it will be joined to
-
# the tag to produce a namespace:tag combination. It is easier to
-
# show this than describe it.
-
#
-
# xml.SOAP :Envelope do ... end
-
#
-
# Just put a space before the colon in a namespace to produce the
-
# right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
-
# "<tt>xml.SOAP :Envelope</tt>")
-
#
-
# * XmlMarkup builds the markup in any object (called a _target_)
-
# that accepts the <tt><<</tt> method. If no target is given,
-
# then XmlMarkup defaults to a string target.
-
#
-
# Examples:
-
#
-
# xm = Builder::XmlMarkup.new
-
# result = xm.title("yada")
-
# # result is a string containing the markup.
-
#
-
# buffer = ""
-
# xm = Builder::XmlMarkup.new(buffer)
-
# # The markup is appended to buffer (using <<)
-
#
-
# xm = Builder::XmlMarkup.new(STDOUT)
-
# # The markup is written to STDOUT (using <<)
-
#
-
# xm = Builder::XmlMarkup.new
-
# x2 = Builder::XmlMarkup.new(:target=>xm)
-
# # Markup written to +x2+ will be send to +xm+.
-
#
-
# * Indentation is enabled by providing the number of spaces to
-
# indent for each level as a second argument to XmlBuilder.new.
-
# Initial indentation may be specified using a third parameter.
-
#
-
# Example:
-
#
-
# xm = Builder.new(:indent=>2)
-
# # xm will produce nicely formatted and indented XML.
-
#
-
# xm = Builder.new(:indent=>2, :margin=>4)
-
# # xm will produce nicely formatted and indented XML with 2
-
# # spaces per indent and an over all indentation level of 4.
-
#
-
# builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
-
# builder.name { |b| b.first("Jim"); b.last("Weirich) }
-
# # prints:
-
# # <name>
-
# # <first>Jim</first>
-
# # <last>Weirich</last>
-
# # </name>
-
#
-
# * The instance_eval implementation which forces self to refer to
-
# the message receiver as self is now obsolete. We now use normal
-
# block calls to execute the markup block. This means that all
-
# markup methods must now be explicitly send to the xml builder.
-
# For instance, instead of
-
#
-
# xml.div { strong("text") }
-
#
-
# you need to write:
-
#
-
# xml.div { xml.strong("text") }
-
#
-
# Although more verbose, the subtle change in semantics within the
-
# block was found to be prone to error. To make this change a
-
# little less cumbersome, the markup block now gets the markup
-
# object sent as an argument, allowing you to use a shorter alias
-
# within the block.
-
#
-
# For example:
-
#
-
# xml_builder = Builder::XmlMarkup.new
-
# xml_builder.div { |xml|
-
# xml.stong("text")
-
# }
-
#
-
1
class XmlMarkup < XmlBase
-
-
# Create an XML markup builder. Parameters are specified by an
-
# option hash.
-
#
-
# :target=><em>target_object</em>::
-
# Object receiving the markup. +target_object+ must respond to
-
# the <tt><<(<em>a_string</em>)</tt> operator and return
-
# itself. The default target is a plain string target.
-
#
-
# :indent=><em>indentation</em>::
-
# Number of spaces used for indentation. The default is no
-
# indentation and no line breaks.
-
#
-
# :margin=><em>initial_indentation_level</em>::
-
# Amount of initial indentation (specified in levels, not
-
# spaces).
-
#
-
# :escape_attrs=><em>OBSOLETE</em>::
-
# The :escape_attrs option is no longer supported by builder
-
# (and will be quietly ignored). String attribute values are
-
# now automatically escaped. If you need unescaped attribute
-
# values (perhaps you are using entities in the attribute
-
# values), then give the value as a Symbol. This allows much
-
# finer control over escaping attribute values.
-
#
-
1
def initialize(options={})
-
36
indent = options[:indent] || 0
-
36
margin = options[:margin] || 0
-
36
super(indent, margin)
-
36
@target = options[:target] || ""
-
end
-
-
# Return the target of the builder.
-
1
def target!
-
@target
-
end
-
-
1
def comment!(comment_text)
-
_ensure_no_block ::Kernel::block_given?
-
_special("<!-- ", " -->", comment_text, nil)
-
end
-
-
# Insert an XML declaration into the XML markup.
-
#
-
# For example:
-
#
-
# xml.declare! :ELEMENT, :blah, "yada"
-
# # => <!ELEMENT blah "yada">
-
1
def declare!(inst, *args, &block)
-
_indent
-
@target << "<!#{inst}"
-
args.each do |arg|
-
case arg
-
when ::String
-
@target << %{ "#{arg}"} # " WART
-
when ::Symbol
-
@target << " #{arg}"
-
end
-
end
-
if ::Kernel::block_given?
-
@target << " ["
-
_newline
-
_nested_structures(block)
-
@target << "]"
-
end
-
@target << ">"
-
_newline
-
end
-
-
# Insert a processing instruction into the XML markup. E.g.
-
#
-
# For example:
-
#
-
# xml.instruct!
-
# #=> <?xml version="1.0" encoding="UTF-8"?>
-
# xml.instruct! :aaa, :bbb=>"ccc"
-
# #=> <?aaa bbb="ccc"?>
-
#
-
# Note: If the encoding is setup to "UTF-8" and the value of
-
# $KCODE is "UTF8", then builder will emit UTF-8 encoded strings
-
# rather than the entity encoding normally used.
-
1
def instruct!(directive_tag=:xml, attrs={})
-
32
_ensure_no_block ::Kernel::block_given?
-
32
if directive_tag == :xml
-
32
a = { :version=>"1.0", :encoding=>"UTF-8" }
-
32
attrs = a.merge attrs
-
32
@encoding = attrs[:encoding].downcase
-
end
-
_special(
-
32
"<?#{directive_tag}",
-
"?>",
-
nil,
-
attrs,
-
[:version, :encoding, :standalone])
-
end
-
-
# Insert a CDATA section into the XML markup.
-
#
-
# For example:
-
#
-
# xml.cdata!("text to be included in cdata")
-
# #=> <![CDATA[text to be included in cdata]]>
-
#
-
1
def cdata!(text)
-
_ensure_no_block ::Kernel::block_given?
-
_special("<![CDATA[", "]]>", text.gsub(']]>', ']]]]><![CDATA[>'), nil)
-
end
-
-
1
private
-
-
# NOTE: All private methods of a builder object are prefixed when
-
# a "_" character to avoid possible conflict with XML tag names.
-
-
# Insert text directly in to the builder's target.
-
1
def _text(text)
-
517
@target << text
-
end
-
-
# Insert special instruction.
-
1
def _special(open, close, data=nil, attrs=nil, order=[])
-
32
_indent
-
32
@target << open
-
32
@target << data if data
-
32
_insert_attributes(attrs, order) if attrs
-
32
@target << close
-
32
_newline
-
end
-
-
# Start an XML tag. If <tt>end_too</tt> is true, then the start
-
# tag is also the end tag (e.g. <br/>
-
1
def _start_tag(sym, attrs, end_too=false)
-
281
@target << "<#{sym}"
-
281
_insert_attributes(attrs)
-
281
@target << "/" if end_too
-
281
@target << ">"
-
end
-
-
# Insert an ending tag.
-
1
def _end_tag(sym)
-
280
@target << "</#{sym}>"
-
end
-
-
# Insert the attributes (given in the hash).
-
1
def _insert_attributes(attrs, order=[])
-
313
return if attrs.nil?
-
265
order.each do |k|
-
96
v = attrs[k]
-
96
@target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
-
end
-
265
attrs.each do |k, v|
-
209
@target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
-
end
-
end
-
-
1
def _attr_value(value)
-
209
case value
-
when ::Symbol
-
value.to_s
-
else
-
209
_escape_attribute(value.to_s)
-
end
-
end
-
-
1
def _ensure_no_block(got_block)
-
32
if got_block
-
::Kernel::raise IllegalBlockError.new(
-
"Blocks are not allowed on XML instructions"
-
)
-
end
-
end
-
-
end
-
-
end
-
1
require 'i18n/version'
-
1
require 'i18n/exceptions'
-
1
require 'i18n/interpolate/ruby'
-
-
1
module I18n
-
1
autoload :Backend, 'i18n/backend'
-
1
autoload :Config, 'i18n/config'
-
1
autoload :Gettext, 'i18n/gettext'
-
1
autoload :Locale, 'i18n/locale'
-
1
autoload :Tests, 'i18n/tests'
-
-
1
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :rescue_format]
-
1
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
-
-
extend Module.new {
-
# Gets I18n configuration object.
-
1
def config
-
7154
Thread.current[:i18n_config] ||= I18n::Config.new
-
end
-
-
# Sets I18n configuration object.
-
1
def config=(value)
-
Thread.current[:i18n_config] = value
-
end
-
-
# Write methods which delegates to the configuration object
-
1
%w(locale backend default_locale available_locales default_separator
-
exception_handler load_path).each do |method|
-
7
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
-
def #{method}
-
config.#{method}
-
end
-
-
def #{method}=(value)
-
config.#{method} = (value)
-
end
-
DELEGATORS
-
end
-
-
# Tells the backend to reload translations. Used in situations like the
-
# Rails development environment. Backends can implement whatever strategy
-
# is useful.
-
1
def reload!
-
config.backend.reload!
-
end
-
-
# Translates, pluralizes and interpolates a given key using a given locale,
-
# scope, and default, as well as interpolation values.
-
#
-
# *LOOKUP*
-
#
-
# Translation data is organized as a nested hash using the upper-level keys
-
# as namespaces. <em>E.g.</em>, ActionView ships with the translation:
-
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Translations can be looked up at any level of this hash using the key argument
-
# and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
-
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Key can be either a single key or a dot-separated key (both Strings and Symbols
-
# work). <em>E.g.</em>, the short format can be looked up using both:
-
# I18n.t 'date.formats.short'
-
# I18n.t :'date.formats.short'
-
#
-
# Scope can be either a single key, a dot-separated key or an array of keys
-
# or dot-separated keys. Keys and scopes can be combined freely. So these
-
# examples will all look up the same short date format:
-
# I18n.t 'date.formats.short'
-
# I18n.t 'formats.short', :scope => 'date'
-
# I18n.t 'short', :scope => 'date.formats'
-
# I18n.t 'short', :scope => %w(date formats)
-
#
-
# *INTERPOLATION*
-
#
-
# Translations can contain interpolation variables which will be replaced by
-
# values passed to #translate as part of the options hash, with the keys matching
-
# the interpolation variable names.
-
#
-
# <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
-
# value for the key +bar+ will be interpolated into the translation:
-
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
-
#
-
# *PLURALIZATION*
-
#
-
# Translation data can contain pluralized translations. Pluralized translations
-
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
-
#
-
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
-
# pluralization rules. Other algorithms can be supported by custom backends.
-
#
-
# This returns the singular version of a pluralized translation:
-
# I18n.t :foo, :count => 1 # => 'Foo'
-
#
-
# These both return the plural version of a pluralized translation:
-
# I18n.t :foo, :count => 0 # => 'Foos'
-
# I18n.t :foo, :count => 2 # => 'Foos'
-
#
-
# The <tt>:count</tt> option can be used both for pluralization and interpolation.
-
# <em>E.g.</em>, with the translation
-
# <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
-
# be interpolated to the pluralized translation:
-
# I18n.t :foo, :count => 1 # => '1 foo'
-
#
-
# *DEFAULTS*
-
#
-
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
-
# I18n.t :foo, :default => 'default'
-
#
-
# This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
-
# translation for <tt>:foo</tt> was found:
-
# I18n.t :foo, :default => :bar
-
#
-
# Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
-
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
-
# I18n.t :foo, :default => [:bar, 'default']
-
#
-
# *BULK LOOKUP*
-
#
-
# This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
-
# I18n.t [:foo, :bar]
-
#
-
# Can be used with dot-separated nested keys:
-
# I18n.t [:'baz.foo', :'baz.bar']
-
#
-
# Which is the same as using a scope option:
-
# I18n.t [:foo, :bar], :scope => :baz
-
#
-
# *LAMBDAS*
-
#
-
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
-
# called and passed the key and options.
-
#
-
# E.g. assuming the key <tt>:salutation</tt> resolves to:
-
# lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
-
#
-
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
-
#
-
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
-
# a cache layer is put in front of I18n.translate it will generate a cache key
-
# from the argument values passed to #translate. Therefor your lambdas should
-
# always return the same translations/values per unique combination of argument
-
# values.
-
1
def translate(*args)
-
2526
options = args.last.is_a?(Hash) ? args.pop : {}
-
2526
key = args.shift
-
2526
backend = config.backend
-
2526
locale = options.delete(:locale) || config.locale
-
2526
handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise
-
-
2526
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
-
-
2526
result = catch(:exception) do
-
2526
if key.is_a?(Array)
-
key.map { |k| backend.translate(locale, k, options) }
-
else
-
2526
backend.translate(locale, key, options)
-
end
-
end
-
2526
result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result
-
end
-
1
alias :t :translate
-
-
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
-
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
-
1
def translate!(key, options={})
-
translate(key, options.merge(:raise => true))
-
end
-
1
alias :t! :translate!
-
-
# Transliterates UTF-8 characters to ASCII. By default this method will
-
# transliterate only Latin strings to an ASCII approximation:
-
#
-
# I18n.transliterate("Ærøskøbing")
-
# # => "AEroskobing"
-
#
-
# I18n.transliterate("日本語")
-
# # => "???"
-
#
-
# It's also possible to add support for per-locale transliterations. I18n
-
# expects transliteration rules to be stored at
-
# <tt>i18n.transliterate.rule</tt>.
-
#
-
# Transliteration rules can either be a Hash or a Proc. Procs must accept a
-
# single string argument. Hash rules inherit the default transliteration
-
# rules, while Procs do not.
-
#
-
# *Examples*
-
#
-
# Setting a Hash in <locale>.yml:
-
#
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# Setting a Hash using Ruby:
-
#
-
# store_translations(:de, :i18n => {
-
# :transliterate => {
-
# :rule => {
-
# "ü" => "ue",
-
# "ö" => "oe"
-
# }
-
# }
-
# )
-
#
-
# Setting a Proc:
-
#
-
# translit = lambda {|string| MyTransliterator.transliterate(string) }
-
# store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
-
#
-
# Transliterating strings:
-
#
-
# I18n.locale = :en
-
# I18n.transliterate("Jürgen") # => "Jurgen"
-
# I18n.locale = :de
-
# I18n.transliterate("Jürgen") # => "Juergen"
-
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
-
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
-
1
def transliterate(*args)
-
options = args.pop if args.last.is_a?(Hash)
-
key = args.shift
-
locale = options && options.delete(:locale) || config.locale
-
handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
-
replacement = options && options.delete(:replacement)
-
config.backend.transliterate(locale, key, replacement)
-
rescue I18n::ArgumentError => exception
-
handle_exception(handling, exception, locale, key, options || {})
-
end
-
-
# Localizes certain objects, such as dates and numbers to local formatting.
-
1
def localize(object, options = {})
-
locale = options.delete(:locale) || config.locale
-
format = options.delete(:format) || :default
-
config.backend.localize(locale, object, format, options)
-
end
-
1
alias :l :localize
-
-
# Executes block with given I18n.locale set.
-
1
def with_locale(tmp_locale = nil)
-
if tmp_locale
-
current_locale = self.locale
-
self.locale = tmp_locale
-
end
-
yield
-
ensure
-
self.locale = current_locale if tmp_locale
-
end
-
-
# Merges the given locale, key and scope into a single array of keys.
-
# Splits keys that contain dots into multiple keys. Makes sure all
-
# keys are Symbols.
-
1
def normalize_keys(locale, key, scope, separator = nil)
-
2526
separator ||= I18n.default_separator
-
-
2526
keys = []
-
2526
keys.concat normalize_key(locale, separator)
-
2526
keys.concat normalize_key(scope, separator)
-
2526
keys.concat normalize_key(key, separator)
-
2526
keys
-
end
-
-
# making these private until Ruby 1.9.2 can send to protected methods again
-
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
-
1
private
-
-
# Any exceptions thrown in translate will be sent to the @@exception_handler
-
# which can be a Symbol, a Proc or any other Object unless they're forced to
-
# be raised or thrown (MissingTranslation).
-
#
-
# If exception_handler is a Symbol then it will simply be sent to I18n as
-
# a method call. A Proc will simply be called. In any other case the
-
# method #call will be called on the exception_handler object.
-
#
-
# Examples:
-
#
-
# I18n.exception_handler = :default_exception_handler # this is the default
-
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = lambda { |*args| ... } # a lambda
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = I18nExceptionHandler.new # an object
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
1
def handle_exception(handling, exception, locale, key, options)
-
1187
case handling
-
when :raise
-
raise(exception.respond_to?(:to_exception) ? exception.to_exception : exception)
-
when :throw
-
1187
throw :exception, exception
-
else
-
case handler = options[:exception_handler] || config.exception_handler
-
when Symbol
-
send(handler, exception, locale, key, options)
-
else
-
handler.call(exception, locale, key, options)
-
end
-
end
-
end
-
-
1
def normalize_key(key, separator)
-
7580
normalized_key_cache[separator][key] ||=
-
case key
-
when Array
-
3
key.map { |k| normalize_key(k, separator) }.flatten
-
else
-
274
keys = key.to_s.split(separator)
-
274
keys.delete('')
-
1512
keys.map! { |k| k.to_sym }
-
274
keys
-
end
-
end
-
-
1
def normalized_key_cache
-
7581
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
-
end
-
-
# DEPRECATED. Use I18n.normalize_keys instead.
-
1
def normalize_translation_keys(locale, key, scope, separator = nil)
-
puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead."
-
normalize_keys(locale, key, scope, separator)
-
end
-
-
# DEPRECATED. Please use the I18n::ExceptionHandler class instead.
-
1
def default_exception_handler(exception, locale, key, options)
-
puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " +
-
"(an instance of which is set to I18n.exception_handler by default)."
-
exception.is_a?(MissingTranslation) ? exception.message : raise(exception)
-
end
-
1
}
-
end
-
1
module I18n
-
1
module Backend
-
1
autoload :Base, 'i18n/backend/base'
-
1
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
-
1
autoload :Cache, 'i18n/backend/cache'
-
1
autoload :Cascade, 'i18n/backend/cascade'
-
1
autoload :Chain, 'i18n/backend/chain'
-
1
autoload :Fallbacks, 'i18n/backend/fallbacks'
-
1
autoload :Flatten, 'i18n/backend/flatten'
-
1
autoload :Gettext, 'i18n/backend/gettext'
-
1
autoload :KeyValue, 'i18n/backend/key_value'
-
1
autoload :Memoize, 'i18n/backend/memoize'
-
1
autoload :Metadata, 'i18n/backend/metadata'
-
1
autoload :Pluralization, 'i18n/backend/pluralization'
-
1
autoload :Simple, 'i18n/backend/simple'
-
1
autoload :Transliterator, 'i18n/backend/transliterator'
-
end
-
end
-
1
require 'yaml'
-
1
require 'i18n/core_ext/hash'
-
1
require 'i18n/core_ext/kernel/surpress_warnings'
-
-
1
module I18n
-
1
module Backend
-
1
module Base
-
1
include I18n::Backend::Transliterator
-
-
# Accepts a list of paths to translation files. Loads translations from
-
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
-
# for details.
-
1
def load_translations(*filenames)
-
71
filenames = I18n.load_path if filenames.empty?
-
109
filenames.flatten.each { |filename| load_file(filename) }
-
end
-
-
# This method receives a locale, a data hash and options for storing translations.
-
# Should be implemented
-
1
def store_translations(locale, data, options = {})
-
raise NotImplementedError
-
end
-
-
1
def translate(locale, key, options = {})
-
2526
raise InvalidLocale.new(locale) unless locale
-
2526
entry = key && lookup(locale, key, options[:scope], options)
-
-
2526
if options.empty?
-
entry = resolve(locale, key, entry, options)
-
else
-
2526
count, default = options.values_at(:count, :default)
-
2526
values = options.except(*RESERVED_KEYS)
-
2526
entry = entry.nil? && default ?
-
default(locale, key, default, options) : resolve(locale, key, entry, options)
-
end
-
-
2526
throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil?
-
1339
entry = entry.dup if entry.is_a?(String)
-
-
1339
entry = pluralize(locale, entry, count) if count
-
1339
entry = interpolate(locale, entry, values) if values
-
1339
entry
-
end
-
-
# Acts the same as +strftime+, but uses a localized version of the
-
# format string. Takes a key from the date/time formats translations as
-
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
-
1
def localize(locale, object, format = :default, options = {})
-
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
-
-
if Symbol === format
-
key = format
-
type = object.respond_to?(:sec) ? 'time' : 'date'
-
options = options.merge(:raise => true, :object => object, :locale => locale)
-
format = I18n.t(:"#{type}.formats.#{key}", options)
-
end
-
-
# format = resolve(locale, object, format, options)
-
format = format.to_s.gsub(/%[aAbBp]/) do |match|
-
case match
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
-
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
-
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
-
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
-
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour
-
end
-
end
-
-
object.strftime(format)
-
end
-
-
# Returns an array of locales for which translations are available
-
# ignoring the reserved translation meta data key :i18n.
-
1
def available_locales
-
raise NotImplementedError
-
end
-
-
1
def reload!
-
@skip_syntax_deprecation = false
-
end
-
-
1
protected
-
-
# The method which actually looks up for the translation in the store.
-
1
def lookup(locale, key, scope = [], options = {})
-
raise NotImplementedError
-
end
-
-
# Evaluates defaults.
-
# If given subject is an Array, it walks the array and returns the
-
# first translation that can be resolved. Otherwise it tries to resolve
-
# the translation directly.
-
1
def default(locale, object, subject, options = {})
-
4049
options = options.dup.reject { |key, value| key == :default }
-
1007
case subject
-
when Array
-
subject.each do |item|
-
2192
result = resolve(locale, object, item, options) and return result
-
1005
end and nil
-
else
-
2
resolve(locale, object, subject, options)
-
end
-
end
-
-
# Resolves a translation.
-
# If the given subject is a Symbol, it will be translated with the
-
# given options. If it is a Proc then it will be evaluated. All other
-
# subjects will be returned directly.
-
1
def resolve(locale, object, subject, options = {})
-
3714
return subject if options[:resolve] == false
-
3714
result = catch(:exception) do
-
3714
case subject
-
when Symbol
-
1452
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
-
when Proc
-
1
date_or_time = options.delete(:object) || object
-
1
resolve(locale, object, subject.call(date_or_time, options))
-
else
-
2261
subject
-
end
-
end
-
3714
result unless result.is_a?(MissingTranslation)
-
end
-
-
# Picks a translation from an array according to English pluralization
-
# rules. It will pick the first translation if count is not equal to 1
-
# and the second translation if it is equal to 1. Other backends can
-
# implement more flexible or complex pluralization rules.
-
1
def pluralize(locale, entry, count)
-
863
return entry unless entry.is_a?(Hash) && count
-
-
key = :zero if count == 0 && entry.has_key?(:zero)
-
key ||= count == 1 ? :one : :other
-
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
-
entry[key]
-
end
-
-
# Interpolates values into a given string.
-
#
-
# interpolate "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X'
-
# # => "file test.txt opened by %{user}"
-
1
def interpolate(locale, string, values = {})
-
1339
if string.is_a?(::String) && !values.empty?
-
1339
I18n.interpolate(string, values)
-
else
-
string
-
end
-
end
-
-
# Loads a single translations file by delegating to #load_rb or
-
# #load_yml depending on the file extension and directly merges the
-
# data to the existing translations. Raises I18n::UnknownFileType
-
# for all other file extensions.
-
1
def load_file(filename)
-
38
type = File.extname(filename).tr('.', '').downcase
-
38
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
-
38
data = send(:"load_#{type}", filename)
-
38
raise InvalidLocaleData.new(filename) unless data.is_a?(Hash)
-
76
data.each { |locale, d| store_translations(locale, d || {}) }
-
end
-
-
# Loads a plain Ruby translations file. eval'ing the file must yield
-
# a Hash containing translation data with locales as toplevel keys.
-
1
def load_rb(filename)
-
eval(IO.read(filename), binding, filename)
-
end
-
-
# Loads a YAML translations file. The data must have locales as
-
# toplevel keys.
-
1
def load_yml(filename)
-
38
begin
-
38
YAML.load_file(filename)
-
rescue TypeError
-
nil
-
rescue SyntaxError
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
module Backend
-
# A simple backend that reads translations from YAML files and stores them in
-
# an in-memory hash. Relies on the Base backend.
-
#
-
# The implementation is provided by a Implementation module allowing to easily
-
# extend Simple backend's behavior by including modules. E.g.:
-
#
-
# module I18n::Backend::Pluralization
-
# def pluralize(*args)
-
# # extended pluralization logic
-
# super
-
# end
-
# end
-
#
-
# I18n::Backend::Simple.include(I18n::Backend::Pluralization)
-
1
class Simple
-
3
(class << self; self; end).class_eval { public :include }
-
-
1
module Implementation
-
1
include Base
-
-
1
def initialized?
-
2526
@initialized ||= false
-
end
-
-
# Stores translations for the given locale in memory.
-
# This uses a deep merge for the translations hash, so existing
-
# translations will be overwritten by new ones only at the deepest
-
# level of the hash.
-
1
def store_translations(locale, data, options = {})
-
243
locale = locale.to_sym
-
243
translations[locale] ||= {}
-
243
data = data.deep_symbolize_keys
-
243
translations[locale].deep_merge!(data)
-
end
-
-
# Get available locales from the translations hash
-
1
def available_locales
-
init_translations unless initialized?
-
translations.inject([]) do |locales, (locale, data)|
-
locales << locale unless (data.keys - [:i18n]).empty?
-
locales
-
end
-
end
-
-
# Clean up translations hash and set initialized to false on reload!
-
1
def reload!
-
@initialized = false
-
@translations = nil
-
super
-
end
-
-
1
protected
-
-
1
def init_translations
-
71
load_translations
-
71
@initialized = true
-
end
-
-
1
def translations
-
3012
@translations ||= {}
-
end
-
-
# Looks up a translation from the translations hash. Returns nil if
-
# eiher key is nil, or locale, scope or key do not exist as a key in the
-
# nested translations hash. Splits keys or scopes containing dots
-
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
-
# <tt>%w(currency format)</tt>.
-
1
def lookup(locale, key, scope = [], options = {})
-
2526
init_translations unless initialized?
-
2526
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
-
-
2526
keys.inject(translations) do |result, _key|
-
7841
_key = _key.to_sym
-
7841
return nil unless result.is_a?(Hash) && result.has_key?(_key)
-
5647
result = result[_key]
-
5647
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
-
5647
result
-
end
-
end
-
end
-
-
1
include Implementation
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module I18n
-
1
module Backend
-
1
module Transliterator
-
1
DEFAULT_REPLACEMENT_CHAR = "?"
-
-
# Given a locale and a UTF-8 string, return the locale's ASCII
-
# approximation for the string.
-
1
def transliterate(locale, string, replacement = nil)
-
@transliterators ||= {}
-
@transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
-
:locale => locale, :resolve => false, :default => {})
-
@transliterators[locale].transliterate(string, replacement)
-
end
-
-
# Get a transliterator instance.
-
1
def self.get(rule = nil)
-
if !rule || rule.kind_of?(Hash)
-
HashTransliterator.new(rule)
-
elsif rule.kind_of? Proc
-
ProcTransliterator.new(rule)
-
else
-
raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
-
end
-
end
-
-
# A transliterator which accepts a Proc as its transliteration rule.
-
1
class ProcTransliterator
-
1
def initialize(rule)
-
@rule = rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
@rule.call(string)
-
end
-
end
-
-
# A transliterator which accepts a Hash of characters as its translation
-
# rule.
-
1
class HashTransliterator
-
1
DEFAULT_APPROXIMATIONS = {
-
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
-
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
-
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
-
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
-
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
-
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
-
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
-
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
-
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
-
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
-
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
-
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
-
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
-
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
-
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
-
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
-
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
-
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
-
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
-
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
-
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
-
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
-
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
-
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
-
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
-
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
-
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
-
"Ž"=>"Z", "ž"=>"z"
-
}
-
-
1
def initialize(rule = nil)
-
@rule = rule
-
add DEFAULT_APPROXIMATIONS
-
add rule if rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
string.gsub(/[^\x00-\x7f]/u) do |char|
-
approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
-
end
-
end
-
-
1
private
-
-
1
def approximations
-
@approximations ||= {}
-
end
-
-
# Add transliteration rules to the approximations hash.
-
1
def add(hash)
-
hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
-
approximations.merge! hash
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
class Config
-
# The only configuration value that is not global and scoped to thread is :locale.
-
# It defaults to the default_locale.
-
1
def locale
-
1074
@locale ||= default_locale
-
end
-
-
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
-
1
def locale=(locale)
-
@locale = locale.to_sym rescue nil
-
end
-
-
# Returns the current backend. Defaults to +Backend::Simple+.
-
1
def backend
-
2853
@@backend ||= Backend::Simple.new
-
end
-
-
# Sets the current backend. Used to set a custom backend.
-
1
def backend=(backend)
-
262
@@backend = backend
-
end
-
-
# Returns the current default locale. Defaults to :'en'
-
1
def default_locale
-
1
@@default_locale ||= :en
-
end
-
-
# Sets the current default locale. Used to set a custom default locale.
-
1
def default_locale=(locale)
-
@@default_locale = locale.to_sym rescue nil
-
end
-
-
# Returns an array of locales for which translations are available.
-
# Unless you explicitely set these through I18n.available_locales=
-
# the call will be delegated to the backend.
-
1
def available_locales
-
@@available_locales ||= nil
-
@@available_locales || backend.available_locales
-
end
-
-
# Sets the available locales.
-
1
def available_locales=(locales)
-
@@available_locales = Array(locales).map { |locale| locale.to_sym }
-
@@available_locales = nil if @@available_locales.empty?
-
end
-
-
# Returns the current default scope separator. Defaults to '.'
-
1
def default_separator
-
2526
@@default_separator ||= '.'
-
end
-
-
# Sets the current default scope separator.
-
1
def default_separator=(separator)
-
@@default_separator = separator
-
end
-
-
# Return the current exception handler. Defaults to :default_exception_handler.
-
1
def exception_handler
-
@@exception_handler ||= ExceptionHandler.new
-
end
-
-
# Sets the exception handler.
-
1
def exception_handler=(exception_handler)
-
@@exception_handler = exception_handler
-
end
-
-
# Allow clients to register paths providing translation data sources. The
-
# backend defines acceptable sources.
-
#
-
# E.g. the provided SimpleBackend accepts a list of paths to translation
-
# files which are either named *.rb and contain plain Ruby Hashes or are
-
# named *.yml and contain YAML data. So for the SimpleBackend clients may
-
# register translation files like this:
-
# I18n.load_path << 'path/to/locale/en.yml'
-
1
def load_path
-
439
@@load_path ||= []
-
end
-
-
# Sets the load path instance. Custom implementations are expected to
-
# behave like a Ruby Array.
-
1
def load_path=(load_path)
-
@@load_path = load_path
-
end
-
end
-
end
-
1
class Hash
-
def slice(*keep_keys)
-
h = {}
-
keep_keys.each { |key| h[key] = fetch(key) }
-
h
-
1
end unless Hash.method_defined?(:slice)
-
-
def except(*less_keys)
-
slice(*keys - less_keys)
-
1
end unless Hash.method_defined?(:except)
-
-
def deep_symbolize_keys
-
inject({}) { |result, (key, value)|
-
value = value.deep_symbolize_keys if value.is_a?(Hash)
-
result[(key.to_sym rescue key) || key] = value
-
result
-
}
-
1
end unless Hash.method_defined?(:deep_symbolize_keys)
-
-
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
-
1
MERGER = proc do |key, v1, v2|
-
83
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
-
end
-
-
def deep_merge!(data)
-
243
merge!(data, &MERGER)
-
1
end unless Hash.method_defined?(:deep_merge!)
-
end
-
-
1
module Kernel
-
1
def suppress_warnings
-
original_verbosity = $VERBOSE
-
$VERBOSE = nil
-
result = yield
-
$VERBOSE = original_verbosity
-
result
-
end
-
end
-
1
module I18n
-
# Handles exceptions raised in the backend. All exceptions except for
-
# MissingTranslationData exceptions are re-thrown. When a MissingTranslationData
-
# was caught the handler returns an error message string containing the key/scope.
-
# Note that the exception handler is not called when the option :throw was given.
-
1
class ExceptionHandler
-
include Module.new {
-
1
def call(exception, locale, key, options)
-
if exception.is_a?(MissingTranslation)
-
options[:rescue_format] == :html ? exception.html_message : exception.message
-
elsif exception.is_a?(Exception)
-
raise exception
-
else
-
throw :exception, exception
-
end
-
end
-
1
}
-
end
-
-
1
class ArgumentError < ::ArgumentError; end
-
-
1
class InvalidLocale < ArgumentError
-
1
attr_reader :locale
-
1
def initialize(locale)
-
@locale = locale
-
super "#{locale.inspect} is not a valid locale"
-
end
-
end
-
-
1
class InvalidLocaleData < ArgumentError
-
1
attr_reader :filename
-
1
def initialize(filename)
-
@filename = filename
-
super "can not load translations from #{filename}, expected it to return a hash, but does not"
-
end
-
end
-
-
1
class MissingTranslation
-
1
module Base
-
1
attr_reader :locale, :key, :options
-
-
1
def initialize(locale, key, options = nil)
-
1187
@key, @locale, @options = key, locale, options.dup || {}
-
4187
options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
-
end
-
-
1
def html_message
-
key = keys.last.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
-
%(<span class="translation_missing" title="translation missing: #{keys.join('.')}">#{key}</span>)
-
end
-
-
1
def keys
-
@keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
-
keys << 'no key' if keys.size < 2
-
end
-
end
-
-
1
def message
-
"translation missing: #{keys.join('.')}"
-
end
-
1
alias :to_s :message
-
-
1
def to_exception
-
MissingTranslationData.new(locale, key, options)
-
end
-
end
-
-
1
include Base
-
end
-
-
1
class MissingTranslationData < ArgumentError
-
1
include MissingTranslation::Base
-
end
-
-
1
class InvalidPluralizationData < ArgumentError
-
1
attr_reader :entry, :count
-
1
def initialize(entry, count)
-
@entry, @count = entry, count
-
super "translation data #{entry.inspect} can not be used with :count => #{count}"
-
end
-
end
-
-
1
class MissingInterpolationArgument < ArgumentError
-
1
attr_reader :values, :string
-
1
def initialize(values, string)
-
@values, @string = values, string
-
super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
-
end
-
end
-
-
1
class ReservedInterpolationKey < ArgumentError
-
1
attr_reader :key, :string
-
1
def initialize(key, string)
-
@key, @string = key, string
-
super "reserved key #{key.inspect} used in #{string.inspect}"
-
end
-
end
-
-
1
class UnknownFileType < ArgumentError
-
1
attr_reader :type, :filename
-
1
def initialize(type, filename)
-
@type, @filename = type, filename
-
super "can not load translations from #{filename}, the file type #{type} is not known"
-
end
-
end
-
end
-
# heavily based on Masao Mutoh's gettext String interpolation extension
-
# http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
-
-
1
module I18n
-
1
INTERPOLATION_PATTERN = Regexp.union(
-
/%%/,
-
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
-
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
-
)
-
-
1
class << self
-
1
def interpolate(string, values)
-
1339
raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ RESERVED_KEYS_PATTERN
-
1339
raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
-
1339
interpolate_hash(string, values)
-
end
-
-
1
def interpolate_hash(string, values)
-
1339
string.gsub(INTERPOLATION_PATTERN) do |match|
-
163
if match == '%%'
-
'%'
-
else
-
163
key = ($1 || $2).to_sym
-
163
value = values.key?(key) ? values[key] : raise(MissingInterpolationArgument.new(values, string))
-
163
value = value.call(values) if value.respond_to?(:call)
-
163
$3 ? sprintf("%#{$3}", value) : value
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
VERSION = "0.6.1"
-
end
-
##
-
# = JavaScript Object Notation (JSON)
-
#
-
# JSON is a lightweight data-interchange format. It is easy for us
-
# humans to read and write. Plus, equally simple for machines to generate or parse.
-
# JSON is completely language agnostic, making it the ideal interchange format.
-
#
-
# Built on two universally available structures:
-
# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array.
-
# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list.
-
#
-
# To read more about JSON visit: http://json.org
-
#
-
# == Parsing JSON
-
#
-
# To parse a JSON string received by another application or generated within
-
# your existing application:
-
#
-
# require 'json'
-
#
-
# my_hash = JSON.parse('{"hello": "goodbye"}')
-
# puts my_hash["hello"] => "goodbye"
-
#
-
# Notice the extra quotes <tt>''</tt> around the hash notation. Ruby expects
-
# the argument to be a string and can't convert objects like a hash or array.
-
#
-
# Ruby converts your string into a hash
-
#
-
# == Generating JSON
-
#
-
# Creating a JSON string for communication or serialization is
-
# just as simple.
-
#
-
# require 'json'
-
#
-
# my_hash = {:hello => "goodbye"}
-
# puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}"
-
#
-
# Or an alternative way:
-
#
-
# require 'json'
-
# puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}"
-
#
-
# <tt>JSON.generate</tt> only allows objects or arrays to be converted
-
# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
-
# even though it acts only as a method for serialization:
-
#
-
# require 'json'
-
#
-
# 1.to_json => "1"
-
#
-
-
1
require 'json/common'
-
1
module JSON
-
1
require 'json/version'
-
-
1
begin
-
1
require 'json/ext'
-
rescue LoadError
-
require 'json/pure'
-
end
-
end
-
1
require 'json/version'
-
1
require 'json/generic_object'
-
-
1
module JSON
-
1
class << self
-
# If _object_ is string-like, parse the string and return the parsed result
-
# as a Ruby data structure. Otherwise generate a JSON text from the Ruby
-
# data structure object and return it.
-
#
-
# The _opts_ argument is passed through to generate/parse respectively. See
-
# generate and parse for their documentation.
-
1
def [](object, opts = {})
-
if object.respond_to? :to_str
-
JSON.parse(object.to_str, opts)
-
else
-
JSON.generate(object, opts)
-
end
-
end
-
-
# Returns the JSON parser class that is used by JSON. This is either
-
# JSON::Ext::Parser or JSON::Pure::Parser.
-
1
attr_reader :parser
-
-
# Set the JSON parser class _parser_ to be used by JSON.
-
1
def parser=(parser) # :nodoc:
-
1
@parser = parser
-
1
remove_const :Parser if JSON.const_defined_in?(self, :Parser)
-
1
const_set :Parser, parser
-
end
-
-
# Return the constant located at _path_. The format of _path_ has to be
-
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
-
# level (absolute namespace path?). If there doesn't exist a constant at
-
# the given path, an ArgumentError is raised.
-
1
def deep_const_get(path) # :nodoc:
-
10
path.to_s.split(/::/).inject(Object) do |p, c|
-
case
-
when c.empty? then p
-
10
when JSON.const_defined_in?(p, c) then p.const_get(c)
-
else
-
begin
-
p.const_missing(c)
-
rescue NameError => e
-
raise ArgumentError, "can't get const #{path}: #{e}"
-
end
-
10
end
-
end
-
end
-
-
# Set the module _generator_ to be used by JSON.
-
1
def generator=(generator) # :nodoc:
-
1
old, $VERBOSE = $VERBOSE, nil
-
1
@generator = generator
-
1
generator_methods = generator::GeneratorMethods
-
1
for const in generator_methods.constants
-
10
klass = deep_const_get(const)
-
10
modul = generator_methods.const_get(const)
-
10
klass.class_eval do
-
10
instance_methods(false).each do |m|
-
477
m.to_s == 'to_json' and remove_method m
-
end
-
10
include modul
-
end
-
end
-
1
self.state = generator::State
-
1
const_set :State, self.state
-
1
const_set :SAFE_STATE_PROTOTYPE, State.new
-
1
const_set :FAST_STATE_PROTOTYPE, State.new(
-
:indent => '',
-
:space => '',
-
:object_nl => "",
-
:array_nl => "",
-
:max_nesting => false
-
)
-
1
const_set :PRETTY_STATE_PROTOTYPE, State.new(
-
:indent => ' ',
-
:space => ' ',
-
:object_nl => "\n",
-
:array_nl => "\n"
-
)
-
ensure
-
1
$VERBOSE = old
-
end
-
-
# Returns the JSON generator module that is used by JSON. This is
-
# either JSON::Ext::Generator or JSON::Pure::Generator.
-
1
attr_reader :generator
-
-
# Returns the JSON generator state class that is used by JSON. This is
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
-
1
attr_accessor :state
-
-
# This is create identifier, which is used to decide if the _json_create_
-
# hook of a class should be called. It defaults to 'json_class'.
-
1
attr_accessor :create_id
-
end
-
1
self.create_id = 'json_class'
-
-
1
NaN = 0.0/0
-
-
1
Infinity = 1.0/0
-
-
1
MinusInfinity = -Infinity
-
-
# The base exception for JSON errors.
-
1
class JSONError < StandardError
-
1
def self.wrap(exception)
-
obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
-
obj.set_backtrace exception.backtrace
-
obj
-
end
-
end
-
-
# This exception is raised if a parser error occurs.
-
1
class ParserError < JSONError; end
-
-
# This exception is raised if the nesting of parsed data structures is too
-
# deep.
-
1
class NestingError < ParserError; end
-
-
# :stopdoc:
-
1
class CircularDatastructure < NestingError; end
-
# :startdoc:
-
-
# This exception is raised if a generator or unparser error occurs.
-
1
class GeneratorError < JSONError; end
-
# For backwards compatibility
-
1
UnparserError = GeneratorError
-
-
# This exception is raised if the required unicode support is missing on the
-
# system. Usually this means that the iconv library is not installed.
-
1
class MissingUnicodeSupport < JSONError; end
-
-
1
module_function
-
-
# Parse the JSON document _source_ into a Ruby data structure and return it.
-
#
-
# _opts_ can have the following
-
# keys:
-
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
-
# structures. Disable depth checking with :max_nesting => false. It defaults
-
# to 19.
-
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
-
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
-
# to false.
-
# * *symbolize_names*: If set to true, returns symbols for the names
-
# (keys) in a JSON object. Otherwise strings are returned. Strings are
-
# the default.
-
# * *create_additions*: If set to false, the Parser doesn't create
-
# additions even if a matching class and create_id was found. This option
-
# defaults to true.
-
# * *object_class*: Defaults to Hash
-
# * *array_class*: Defaults to Array
-
1
def parse(source, opts = {})
-
3
Parser.new(source, opts).parse
-
end
-
-
# Parse the JSON document _source_ into a Ruby data structure and return it.
-
# The bang version of the parse method defaults to the more dangerous values
-
# for the _opts_ hash, so be sure only to parse trusted _source_ documents.
-
#
-
# _opts_ can have the following keys:
-
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
-
# structures. Enable depth checking with :max_nesting => anInteger. The parse!
-
# methods defaults to not doing max depth checking: This can be dangerous
-
# if someone wants to fill up your stack.
-
# * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
-
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
-
# to true.
-
# * *create_additions*: If set to false, the Parser doesn't create
-
# additions even if a matching class and create_id was found. This option
-
# defaults to true.
-
1
def parse!(source, opts = {})
-
opts = {
-
:max_nesting => false,
-
:allow_nan => true
-
}.update(opts)
-
Parser.new(source, opts).parse
-
end
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return
-
# it. _state_ is * a JSON::State object,
-
# * or a Hash like object (responding to to_hash),
-
# * an object convertible into a hash by a to_h method,
-
# that is used as or to configure a State object.
-
#
-
# It defaults to a state object, that creates the shortest possible JSON text
-
# in one line, checks for circular data structures and doesn't allow NaN,
-
# Infinity, and -Infinity.
-
#
-
# A _state_ hash can have the following keys:
-
# * *indent*: a string used to indent levels (default: ''),
-
# * *space*: a string that is put after, a : or , delimiter (default: ''),
-
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
-
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
-
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
-
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
-
# generated, otherwise an exception is thrown if these values are
-
# encountered. This options defaults to false.
-
# * *max_nesting*: The maximum depth of nesting allowed in the data
-
# structures from which JSON is to be generated. Disable depth checking
-
# with :max_nesting => false, it defaults to 19.
-
#
-
# See also the fast_generate for the fastest creation method with the least
-
# amount of sanity checks, and the pretty_generate method for some
-
# defaults for pretty output.
-
1
def generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = SAFE_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state = state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and
-
# later delete them.
-
1
alias unparse generate
-
1
module_function :unparse
-
# :startdoc:
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return it.
-
# This method disables the checks for circles in Ruby objects.
-
#
-
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
-
# _obj_ argument because this will cause JSON to go into an infinite loop.
-
1
def fast_generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = FAST_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
-
1
alias fast_unparse fast_generate
-
1
module_function :fast_unparse
-
# :startdoc:
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return it.
-
# The returned document is a prettier form of the document returned by
-
# #unparse.
-
#
-
# The _opts_ argument can be used to configure the generator. See the
-
# generate method for a more detailed explanation.
-
1
def pretty_generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = PRETTY_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
-
1
alias pretty_unparse pretty_generate
-
1
module_function :pretty_unparse
-
# :startdoc:
-
-
1
class << self
-
# The global default options for the JSON.load method:
-
# :max_nesting: false
-
# :allow_nan: true
-
# :quirks_mode: true
-
1
attr_accessor :load_default_options
-
end
-
1
self.load_default_options = {
-
:max_nesting => false,
-
:allow_nan => true,
-
:quirks_mode => true,
-
}
-
-
# Load a ruby data structure from a JSON _source_ and return it. A source can
-
# either be a string-like object, an IO-like object, or an object responding
-
# to the read method. If _proc_ was given, it will be called with any nested
-
# Ruby object as an argument recursively in depth first order. The default
-
# options for the parser can be changed via the load_default_options method.
-
#
-
# This method is part of the implementation of the load/dump interface of
-
# Marshal and YAML.
-
1
def load(source, proc = nil)
-
opts = load_default_options
-
if source.respond_to? :to_str
-
source = source.to_str
-
elsif source.respond_to? :to_io
-
source = source.to_io.read
-
elsif source.respond_to?(:read)
-
source = source.read
-
end
-
if opts[:quirks_mode] && (source.nil? || source.empty?)
-
source = 'null'
-
end
-
result = parse(source, opts)
-
recurse_proc(result, &proc) if proc
-
result
-
end
-
-
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
-
1
def recurse_proc(result, &proc)
-
case result
-
when Array
-
result.each { |x| recurse_proc x, &proc }
-
proc.call result
-
when Hash
-
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
-
proc.call result
-
else
-
proc.call result
-
end
-
end
-
-
1
alias restore load
-
1
module_function :restore
-
-
1
class << self
-
# The global default options for the JSON.dump method:
-
# :max_nesting: false
-
# :allow_nan: true
-
# :quirks_mode: true
-
1
attr_accessor :dump_default_options
-
end
-
1
self.dump_default_options = {
-
:max_nesting => false,
-
:allow_nan => true,
-
:quirks_mode => true,
-
}
-
-
# Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
-
# the result.
-
#
-
# If anIO (an IO-like object or an object that responds to the write method)
-
# was given, the resulting JSON is written to it.
-
#
-
# If the number of nested arrays or objects exceeds _limit_, an ArgumentError
-
# exception is raised. This argument is similar (but not exactly the
-
# same!) to the _limit_ argument in Marshal.dump.
-
#
-
# The default options for the generator can be changed via the
-
# dump_default_options method.
-
#
-
# This method is part of the implementation of the load/dump interface of
-
# Marshal and YAML.
-
1
def dump(obj, anIO = nil, limit = nil)
-
if anIO and limit.nil?
-
anIO = anIO.to_io if anIO.respond_to?(:to_io)
-
unless anIO.respond_to?(:write)
-
limit = anIO
-
anIO = nil
-
end
-
end
-
opts = JSON.dump_default_options
-
limit and opts.update(:max_nesting => limit)
-
result = generate(obj, opts)
-
if anIO
-
anIO.write result
-
anIO
-
else
-
result
-
end
-
rescue JSON::NestingError
-
raise ArgumentError, "exceed depth limit"
-
end
-
-
# Swap consecutive bytes of _string_ in place.
-
1
def self.swap!(string) # :nodoc:
-
0.upto(string.size / 2) do |i|
-
break unless string[2 * i + 1]
-
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
-
end
-
string
-
end
-
-
# Shortuct for iconv.
-
if ::String.method_defined?(:encode) &&
-
# XXX Rubinius doesn't support ruby 1.9 encoding yet
-
1
defined?(RUBY_ENGINE) && RUBY_ENGINE != 'rbx'
-
then
-
# Encodes string using Ruby's _String.encode_
-
1
def self.iconv(to, from, string)
-
string.encode(to, from)
-
end
-
else
-
require 'iconv'
-
# Encodes string using _iconv_ library
-
def self.iconv(to, from, string)
-
Iconv.conv(to, from, string)
-
end
-
end
-
-
1
if ::Object.method(:const_defined?).arity == 1
-
def self.const_defined_in?(modul, constant)
-
modul.const_defined?(constant)
-
end
-
else
-
1
def self.const_defined_in?(modul, constant)
-
11
modul.const_defined?(constant, false)
-
end
-
end
-
end
-
-
1
module ::Kernel
-
1
private
-
-
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
-
# one line.
-
1
def j(*objs)
-
objs.each do |obj|
-
puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
-
end
-
nil
-
end
-
-
# Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
-
# indentation and over many lines.
-
1
def jj(*objs)
-
objs.each do |obj|
-
puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
-
end
-
nil
-
end
-
-
# If _object_ is string-like, parse the string and return the parsed result as
-
# a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
-
# structure object and return it.
-
#
-
# The _opts_ argument is passed through to generate/parse respectively. See
-
# generate and parse for their documentation.
-
1
def JSON(object, *args)
-
if object.respond_to? :to_str
-
JSON.parse(object.to_str, args.first)
-
else
-
JSON.generate(object, args.first)
-
end
-
end
-
end
-
-
# Extends any Class to include _json_creatable?_ method.
-
1
class ::Class
-
# Returns true if this class can be used to create an instance
-
# from a serialised JSON string. The class has to implement a class
-
# method _json_create_ that expects a hash as first parameter. The hash
-
# should include the required data.
-
1
def json_creatable?
-
respond_to?(:json_create)
-
end
-
end
-
1
if ENV['SIMPLECOV_COVERAGE'].to_i == 1
-
require 'simplecov'
-
SimpleCov.start do
-
add_filter "/tests/"
-
end
-
end
-
1
require 'json/common'
-
-
1
module JSON
-
# This module holds all the modules/classes that implement JSON's
-
# functionality as C extensions.
-
1
module Ext
-
1
require 'json/ext/parser'
-
1
require 'json/ext/generator'
-
1
$DEBUG and warn "Using Ext extension for JSON."
-
1
JSON.parser = Parser
-
1
JSON.generator = Generator
-
end
-
-
1
JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
-
end
-
1
require 'ostruct'
-
-
1
module JSON
-
1
class GenericObject < OpenStruct
-
1
class << self
-
1
alias [] new
-
-
1
def json_create(data)
-
data = data.dup
-
data.delete JSON.create_id
-
self[data]
-
end
-
end
-
-
1
def to_hash
-
table
-
end
-
-
1
def [](name)
-
table[name.to_sym]
-
end
-
-
1
def []=(name, value)
-
__send__ "#{name}=", value
-
end
-
-
1
def |(other)
-
self.class[other.to_hash.merge(to_hash)]
-
end
-
-
1
def as_json(*)
-
{ JSON.create_id => self.class.name }.merge to_hash
-
end
-
-
1
def to_json(*a)
-
as_json.to_json(*a)
-
end
-
end
-
end
-
1
module JSON
-
# JSON version
-
1
VERSION = '1.7.5'
-
4
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
-
1
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
-
1
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
-
1
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
-
end
-
1
module Metaclass
-
end
-
-
1
require "metaclass/version"
-
1
require "metaclass/object_methods"
-
1
module Metaclass::ObjectMethods
-
1
def __metaclass__
-
454
class << self
-
454
self
-
end
-
end
-
end
-
-
1
class Object
-
1
include Metaclass::ObjectMethods
-
end
-
1
module Metaclass
-
1
VERSION = "0.0.1"
-
end
-
1
begin
-
1
require 'rubygems'
-
1
gem 'minitest'
-
rescue Gem::LoadError
-
# do nothing
-
end
-
-
1
require 'minitest/unit'
-
1
require 'minitest/spec'
-
1
require 'minitest/mock'
-
-
1
MiniTest::Unit.autorun
-
1
class MockExpectationError < StandardError # :nodoc:
-
end # omg... worst bug ever. rdoc doesn't allow 1-liners
-
-
##
-
# A simple and clean mock object framework.
-
-
1
module MiniTest
-
-
##
-
# All mock objects are an instance of Mock
-
-
1
class Mock
-
1
alias :__respond_to? :respond_to?
-
-
1
skip_methods = %w(object_id respond_to_missing? inspect === to_s)
-
-
1
instance_methods.each do |m|
-
97
undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/
-
end
-
-
1
def initialize # :nodoc:
-
@expected_calls = Hash.new { |calls, name| calls[name] = [] }
-
@actual_calls = Hash.new { |calls, name| calls[name] = [] }
-
end
-
-
##
-
# Expect that method +name+ is called, optionally with +args+, and returns
-
# +retval+.
-
#
-
# @mock.expect(:meaning_of_life, 42)
-
# @mock.meaning_of_life # => 42
-
#
-
# @mock.expect(:do_something_with, true, [some_obj, true])
-
# @mock.do_something_with(some_obj, true) # => true
-
#
-
# +args+ is compared to the expected args using case equality (ie, the
-
# '===' operator), allowing for less specific expectations.
-
#
-
# @mock.expect(:uses_any_string, true, [String])
-
# @mock.uses_any_string("foo") # => true
-
# @mock.verify # => true
-
#
-
# @mock.expect(:uses_one_string, true, ["foo"]
-
# @mock.uses_one_string("bar") # => true
-
# @mock.verify # => raises MockExpectationError
-
-
1
def expect(name, retval, args=[])
-
raise ArgumentError, "args must be an array" unless Array === args
-
@expected_calls[name] << { :retval => retval, :args => args }
-
self
-
end
-
-
1
def __call name, data # :nodoc:
-
case data
-
when Hash then
-
"#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
-
else
-
data.map { |d| __call name, d }.join ", "
-
end
-
end
-
-
##
-
# Verify that all methods were called as expected. Raises
-
# +MockExpectationError+ if the mock object was not called as
-
# expected.
-
-
1
def verify
-
@expected_calls.each do |name, calls|
-
calls.each do |expected|
-
msg1 = "expected #{__call name, expected}"
-
msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]"
-
-
raise MockExpectationError, msg2 if
-
@actual_calls.has_key?(name) and
-
not @actual_calls[name].include?(expected)
-
-
raise MockExpectationError, msg1 unless
-
@actual_calls.has_key?(name) and
-
@actual_calls[name].include?(expected)
-
end
-
end
-
true
-
end
-
-
1
def method_missing(sym, *args) # :nodoc:
-
unless @expected_calls.has_key?(sym) then
-
raise NoMethodError, "unmocked method %p, expected one of %p" %
-
[sym, @expected_calls.keys.sort_by(&:to_s)]
-
end
-
-
index = @actual_calls[sym].length
-
expected_call = @expected_calls[sym][index]
-
-
unless expected_call then
-
raise MockExpectationError, "No more expects available for %p: %p" %
-
[sym, args]
-
end
-
-
expected_args, retval = expected_call[:args], expected_call[:retval]
-
-
if expected_args.size != args.size then
-
raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
-
[sym, expected_args.size, args.size]
-
end
-
-
fully_matched = expected_args.zip(args).all? { |mod, a|
-
mod === a or mod == a
-
}
-
-
unless fully_matched then
-
raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
-
[sym, args]
-
end
-
-
@actual_calls[sym] << {
-
:retval => retval,
-
:args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a }
-
}
-
-
retval
-
end
-
-
1
def respond_to?(sym, include_private = false) # :nodoc:
-
return true if @expected_calls.has_key?(sym.to_sym)
-
return __respond_to?(sym, include_private)
-
end
-
end
-
end
-
-
1
class Object # :nodoc:
-
-
##
-
# Add a temporary stubbed method replacing +name+ for the duration
-
# of the +block+. If +val_or_callable+ responds to #call, then it
-
# returns the result of calling it, otherwise returns the value
-
# as-is. Cleans up the stub at the end of the +block+.
-
#
-
# def test_stale_eh
-
# obj_under_test = Something.new
-
# refute obj_under_test.stale?
-
#
-
# Time.stub :now, Time.at(0) do
-
# assert obj_under_test.stale?
-
# end
-
# end
-
-
1
def stub name, val_or_callable, &block
-
new_name = "__minitest_stub__#{name}"
-
-
metaclass = class << self; self; end
-
-
if respond_to? name and not methods.map(&:to_s).include? name.to_s then
-
metaclass.send :define_method, name do |*args|
-
super(*args)
-
end
-
end
-
-
metaclass.send :alias_method, new_name, name
-
-
metaclass.send :define_method, name do |*args|
-
if val_or_callable.respond_to? :call then
-
val_or_callable.call(*args)
-
else
-
val_or_callable
-
end
-
end
-
-
yield self
-
ensure
-
metaclass.send :undef_method, name
-
metaclass.send :alias_method, name, new_name
-
metaclass.send :undef_method, new_name
-
end
-
end
-
1
class ParallelEach
-
1
require 'thread'
-
1
include Enumerable
-
-
1
N = (ENV['N'] || 2).to_i
-
-
1
def initialize list
-
1
@queue = Queue.new # *sigh*... the Queue api sucks sooo much...
-
-
1
list.each { |i| @queue << i }
-
3
N.times { @queue << nil }
-
end
-
-
1
def grep pattern
-
self.class.new super
-
end
-
-
1
def each
-
1
threads = N.times.map {
-
2
Thread.new do
-
2
Thread.current.abort_on_exception = true
-
2
while job = @queue.pop
-
yield job
-
end
-
end
-
}
-
1
threads.map(&:join)
-
end
-
end
-
#!/usr/bin/ruby -w
-
-
1
require 'minitest/unit'
-
-
1
class Module # :nodoc:
-
1
def infect_an_assertion meth, new_name, dont_flip = false # :nodoc:
-
# warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
-
29
self.class_eval <<-EOM
-
def #{new_name} *args
-
case
-
when Proc === self then
-
MiniTest::Spec.current.#{meth}(*args, &self)
-
when #{!!dont_flip} then
-
MiniTest::Spec.current.#{meth}(self, *args)
-
else
-
MiniTest::Spec.current.#{meth}(args.first, self, *args[1..-1])
-
end
-
end
-
EOM
-
end
-
-
##
-
# infect_with_assertions has been removed due to excessive clever.
-
# Use infect_an_assertion directly instead.
-
-
1
def infect_with_assertions(pos_prefix, neg_prefix,
-
skip_re,
-
dont_flip_re = /\c0/,
-
map = {})
-
abort "infect_with_assertions is dead. Use infect_an_assertion directly"
-
end
-
end
-
-
1
module Kernel # :nodoc:
-
##
-
# Describe a series of expectations for a given target +desc+.
-
#
-
# TODO: find good tutorial url.
-
#
-
# Defines a test class subclassing from either MiniTest::Spec or
-
# from the surrounding describe's class. The surrounding class may
-
# subclass MiniTest::Spec manually in order to easily share code:
-
#
-
# class MySpec < MiniTest::Spec
-
# # ... shared code ...
-
# end
-
#
-
# class TestStuff < MySpec
-
# it "does stuff" do
-
# # shared code available here
-
# end
-
# describe "inner stuff" do
-
# it "still does stuff" do
-
# # ...and here
-
# end
-
# end
-
# end
-
-
1
def describe desc, additional_desc = nil, &block # :doc:
-
stack = MiniTest::Spec.describe_stack
-
name = [stack.last, desc, additional_desc].compact.join("::")
-
sclas = stack.last || if Class === self && self < MiniTest::Spec then
-
self
-
else
-
MiniTest::Spec.spec_type desc
-
end
-
-
cls = sclas.create name, desc
-
-
stack.push cls
-
cls.class_eval(&block)
-
stack.pop
-
cls
-
end
-
1
private :describe
-
end
-
-
##
-
# MiniTest::Spec -- The faster, better, less-magical spec framework!
-
#
-
# For a list of expectations, see MiniTest::Expectations.
-
-
1
class MiniTest::Spec < MiniTest::Unit::TestCase
-
##
-
# Contains pairs of matchers and Spec classes to be used to
-
# calculate the superclass of a top-level describe. This allows for
-
# automatically customizable spec types.
-
#
-
# See: register_spec_type and spec_type
-
-
1
TYPES = [[//, MiniTest::Spec]]
-
-
##
-
# Register a new type of spec that matches the spec's description.
-
# This method can take either a Regexp and a spec class or a spec
-
# class and a block that takes the description and returns true if
-
# it matches.
-
#
-
# Eg:
-
#
-
# register_spec_type(/Controller$/, MiniTest::Spec::Rails)
-
#
-
# or:
-
#
-
# register_spec_type(MiniTest::Spec::RailsModel) do |desc|
-
# desc.superclass == ActiveRecord::Base
-
# end
-
-
1
def self.register_spec_type(*args, &block)
-
1
if block then
-
1
matcher, klass = block, args.first
-
else
-
matcher, klass = *args
-
end
-
1
TYPES.unshift [matcher, klass]
-
end
-
-
##
-
# Figure out the spec class to use based on a spec's description. Eg:
-
#
-
# spec_type("BlahController") # => MiniTest::Spec::Rails
-
-
1
def self.spec_type desc
-
TYPES.find { |matcher, klass|
-
if matcher.respond_to? :call then
-
matcher.call desc
-
else
-
matcher === desc.to_s
-
end
-
}.last
-
end
-
-
1
@@describe_stack = []
-
1
def self.describe_stack # :nodoc:
-
@@describe_stack
-
end
-
-
##
-
# Returns the children of this spec.
-
-
1
def self.children
-
304
@children ||= []
-
end
-
-
1
def self.nuke_test_methods! # :nodoc:
-
self.public_instance_methods.grep(/^test_/).each do |name|
-
self.send :undef_method, name
-
end
-
end
-
-
##
-
# Define a 'before' action. Inherits the way normal methods should.
-
#
-
# NOTE: +type+ is ignored and is only there to make porting easier.
-
#
-
# Equivalent to MiniTest::Unit::TestCase#setup.
-
-
1
def self.before type = nil, &block
-
define_method :setup do
-
super()
-
self.instance_eval(&block)
-
end
-
end
-
-
##
-
# Define an 'after' action. Inherits the way normal methods should.
-
#
-
# NOTE: +type+ is ignored and is only there to make porting easier.
-
#
-
# Equivalent to MiniTest::Unit::TestCase#teardown.
-
-
1
def self.after type = nil, &block
-
define_method :teardown do
-
self.instance_eval(&block)
-
super()
-
end
-
end
-
-
##
-
# Define an expectation with name +desc+. Name gets morphed to a
-
# proper test method name. For some freakish reason, people who
-
# write specs don't like class inheritence, so this goes way out of
-
# its way to make sure that expectations aren't inherited.
-
#
-
# This is also aliased to #specify and doesn't require a +desc+ arg.
-
#
-
# Hint: If you _do_ want inheritence, use minitest/unit. You can mix
-
# and match between assertions and expectations as much as you want.
-
-
1
def self.it desc = "anonymous", &block
-
304
block ||= proc { skip "(no tests defined)" }
-
-
304
@specs ||= 0
-
304
@specs += 1
-
-
304
name = "test_%04d_%s" % [ @specs, desc ]
-
-
304
define_method name, &block
-
-
304
self.children.each do |mod|
-
mod.send :undef_method, name if mod.public_method_defined? name
-
end
-
-
304
name
-
end
-
-
##
-
# Essentially, define an accessor for +name+ with +block+.
-
#
-
# Why use let instead of def? I honestly don't know.
-
-
1
def self.let name, &block
-
define_method name do
-
@_memoized ||= {}
-
@_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) }
-
end
-
end
-
-
##
-
# Another lazy man's accessor generator. Made even more lazy by
-
# setting the name for you to +subject+.
-
-
1
def self.subject &block
-
let :subject, &block
-
end
-
-
1
def self.create name, desc # :nodoc:
-
cls = Class.new(self) do
-
@name = name
-
@desc = desc
-
-
nuke_test_methods!
-
end
-
-
children << cls
-
-
cls
-
end
-
-
1
def self.to_s # :nodoc:
-
1268
defined?(@name) ? @name : super
-
end
-
-
# :stopdoc:
-
1
class << self
-
1
attr_reader :desc
-
1
alias :specify :it
-
1
alias :name :to_s
-
end
-
# :startdoc:
-
end
-
-
##
-
# It's where you hide your "assertions".
-
-
1
module MiniTest::Expectations
-
##
-
# See MiniTest::Assertions#assert_empty.
-
#
-
# collection.must_be_empty
-
#
-
# :method: must_be_empty
-
-
1
infect_an_assertion :assert_empty, :must_be_empty, :unary
-
-
##
-
# See MiniTest::Assertions#assert_equal
-
#
-
# a.must_equal b
-
#
-
# :method: must_equal
-
-
1
infect_an_assertion :assert_equal, :must_equal
-
-
##
-
# See MiniTest::Assertions#assert_in_delta
-
#
-
# n.must_be_close_to m [, delta]
-
#
-
# :method: must_be_close_to
-
-
1
infect_an_assertion :assert_in_delta, :must_be_close_to
-
-
1
alias :must_be_within_delta :must_be_close_to # :nodoc:
-
-
##
-
# See MiniTest::Assertions#assert_in_epsilon
-
#
-
# n.must_be_within_epsilon m [, epsilon]
-
#
-
# :method: must_be_within_epsilon
-
-
1
infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon
-
-
##
-
# See MiniTest::Assertions#assert_includes
-
#
-
# collection.must_include obj
-
#
-
# :method: must_include
-
-
1
infect_an_assertion :assert_includes, :must_include, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_instance_of
-
#
-
# obj.must_be_instance_of klass
-
#
-
# :method: must_be_instance_of
-
-
1
infect_an_assertion :assert_instance_of, :must_be_instance_of
-
-
##
-
# See MiniTest::Assertions#assert_kind_of
-
#
-
# obj.must_be_kind_of mod
-
#
-
# :method: must_be_kind_of
-
-
1
infect_an_assertion :assert_kind_of, :must_be_kind_of
-
-
##
-
# See MiniTest::Assertions#assert_match
-
#
-
# a.must_match b
-
#
-
# :method: must_match
-
-
1
infect_an_assertion :assert_match, :must_match
-
-
##
-
# See MiniTest::Assertions#assert_nil
-
#
-
# obj.must_be_nil
-
#
-
# :method: must_be_nil
-
-
1
infect_an_assertion :assert_nil, :must_be_nil, :unary
-
-
##
-
# See MiniTest::Assertions#assert_operator
-
#
-
# n.must_be :<=, 42
-
#
-
# This can also do predicates:
-
#
-
# str.must_be :empty?
-
#
-
# :method: must_be
-
-
1
infect_an_assertion :assert_operator, :must_be, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_output
-
#
-
# proc { ... }.must_output out_or_nil [, err]
-
#
-
# :method: must_output
-
-
1
infect_an_assertion :assert_output, :must_output
-
-
##
-
# See MiniTest::Assertions#assert_raises
-
#
-
# proc { ... }.must_raise exception
-
#
-
# :method: must_raise
-
-
1
infect_an_assertion :assert_raises, :must_raise
-
-
##
-
# See MiniTest::Assertions#assert_respond_to
-
#
-
# obj.must_respond_to msg
-
#
-
# :method: must_respond_to
-
-
1
infect_an_assertion :assert_respond_to, :must_respond_to, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_same
-
#
-
# a.must_be_same_as b
-
#
-
# :method: must_be_same_as
-
-
1
infect_an_assertion :assert_same, :must_be_same_as
-
-
##
-
# See MiniTest::Assertions#assert_send
-
# TODO: remove me
-
#
-
# a.must_send
-
#
-
# :method: must_send
-
-
1
infect_an_assertion :assert_send, :must_send
-
-
##
-
# See MiniTest::Assertions#assert_silent
-
#
-
# proc { ... }.must_be_silent
-
#
-
# :method: must_be_silent
-
-
1
infect_an_assertion :assert_silent, :must_be_silent
-
-
##
-
# See MiniTest::Assertions#assert_throws
-
#
-
# proc { ... }.must_throw sym
-
#
-
# :method: must_throw
-
-
1
infect_an_assertion :assert_throws, :must_throw
-
-
##
-
# See MiniTest::Assertions#refute_empty
-
#
-
# collection.wont_be_empty
-
#
-
# :method: wont_be_empty
-
-
1
infect_an_assertion :refute_empty, :wont_be_empty, :unary
-
-
##
-
# See MiniTest::Assertions#refute_equal
-
#
-
# a.wont_equal b
-
#
-
# :method: wont_equal
-
-
1
infect_an_assertion :refute_equal, :wont_equal
-
-
##
-
# See MiniTest::Assertions#refute_in_delta
-
#
-
# n.wont_be_close_to m [, delta]
-
#
-
# :method: wont_be_close_to
-
-
1
infect_an_assertion :refute_in_delta, :wont_be_close_to
-
-
1
alias :wont_be_within_delta :wont_be_close_to # :nodoc:
-
-
##
-
# See MiniTest::Assertions#refute_in_epsilon
-
#
-
# n.wont_be_within_epsilon m [, epsilon]
-
#
-
# :method: wont_be_within_epsilon
-
-
1
infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon
-
-
##
-
# See MiniTest::Assertions#refute_includes
-
#
-
# collection.wont_include obj
-
#
-
# :method: wont_include
-
-
1
infect_an_assertion :refute_includes, :wont_include, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_instance_of
-
#
-
# obj.wont_be_instance_of klass
-
#
-
# :method: wont_be_instance_of
-
-
1
infect_an_assertion :refute_instance_of, :wont_be_instance_of
-
-
##
-
# See MiniTest::Assertions#refute_kind_of
-
#
-
# obj.wont_be_kind_of mod
-
#
-
# :method: wont_be_kind_of
-
-
1
infect_an_assertion :refute_kind_of, :wont_be_kind_of
-
-
##
-
# See MiniTest::Assertions#refute_match
-
#
-
# a.wont_match b
-
#
-
# :method: wont_match
-
-
1
infect_an_assertion :refute_match, :wont_match
-
-
##
-
# See MiniTest::Assertions#refute_nil
-
#
-
# obj.wont_be_nil
-
#
-
# :method: wont_be_nil
-
-
1
infect_an_assertion :refute_nil, :wont_be_nil, :unary
-
-
##
-
# See MiniTest::Assertions#refute_operator
-
#
-
# n.wont_be :<=, 42
-
#
-
# This can also do predicates:
-
#
-
# str.wont_be :empty?
-
#
-
# :method: wont_be
-
-
1
infect_an_assertion :refute_operator, :wont_be, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_respond_to
-
#
-
# obj.wont_respond_to msg
-
#
-
# :method: wont_respond_to
-
-
1
infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_same
-
#
-
# a.wont_be_same_as b
-
#
-
# :method: wont_be_same_as
-
-
1
infect_an_assertion :refute_same, :wont_be_same_as
-
end
-
-
1
class Object # :nodoc:
-
1
include MiniTest::Expectations
-
end
-
1
require 'optparse'
-
1
require 'rbconfig'
-
1
require 'thread' # required for 1.8
-
1
require 'minitest/parallel_each'
-
-
##
-
# Minimal (mostly drop-in) replacement for test-unit.
-
#
-
# :include: README.txt
-
-
1
module MiniTest
-
-
1
def self.const_missing name # :nodoc:
-
case name
-
when :MINI_DIR then
-
msg = "MiniTest::MINI_DIR was removed. Don't violate other's internals."
-
warn "WAR\NING: #{msg}"
-
warn "WAR\NING: Used by #{caller.first}."
-
const_set :MINI_DIR, "bad value"
-
else
-
super
-
end
-
end
-
-
##
-
# Assertion base class
-
-
1
class Assertion < Exception; end
-
-
##
-
# Assertion raised when skipping a test
-
-
1
class Skip < Assertion; end
-
-
1
class << self
-
1
attr_accessor :backtrace_filter
-
end
-
-
1
class BacktraceFilter # :nodoc:
-
1
def filter bt
-
45
return ["No backtrace"] unless bt
-
-
45
new_bt = []
-
-
45
unless $DEBUG then
-
45
bt.each do |line|
-
393
break if line =~ /lib\/minitest/
-
348
new_bt << line
-
end
-
-
45
new_bt = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty?
-
45
new_bt = bt.dup if new_bt.empty?
-
else
-
new_bt = bt.dup
-
end
-
-
45
new_bt
-
end
-
end
-
-
1
self.backtrace_filter = BacktraceFilter.new
-
-
1
def self.filter_backtrace bt # :nodoc:
-
45
backtrace_filter.filter bt
-
end
-
-
##
-
# MiniTest Assertions. All assertion methods accept a +msg+ which is
-
# printed if the assertion fails.
-
-
1
module Assertions
-
1
UNDEFINED = Object.new # :nodoc:
-
-
1
def UNDEFINED.inspect # :nodoc:
-
"UNDEFINED" # again with the rdoc bugs... :(
-
end
-
-
##
-
# Returns the diff command to use in #diff. Tries to intelligently
-
# figure out what diff to use.
-
-
1
def self.diff
-
@diff = if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ then
-
"diff.exe -u"
-
else
-
if system("gdiff", __FILE__, __FILE__)
-
"gdiff -u" # solaris and kin suck
-
elsif system("diff", __FILE__, __FILE__)
-
"diff -u"
-
else
-
nil
-
end
-
end unless defined? @diff
-
-
@diff
-
end
-
-
##
-
# Set the diff command to use in #diff.
-
-
1
def self.diff= o
-
@diff = o
-
end
-
-
##
-
# Returns a diff between +exp+ and +act+. If there is no known
-
# diff command or if it doesn't make sense to diff the output
-
# (single line, short output), then it simply returns a basic
-
# comparison between the two.
-
-
1
def diff exp, act
-
require "tempfile"
-
-
expect = mu_pp_for_diff exp
-
butwas = mu_pp_for_diff act
-
result = nil
-
-
need_to_diff =
-
MiniTest::Assertions.diff &&
-
(expect.include?("\n") ||
-
butwas.include?("\n") ||
-
expect.size > 30 ||
-
butwas.size > 30 ||
-
expect == butwas)
-
-
return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
-
need_to_diff
-
-
Tempfile.open("expect") do |a|
-
a.puts expect
-
a.flush
-
-
Tempfile.open("butwas") do |b|
-
b.puts butwas
-
b.flush
-
-
result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}`
-
result.sub!(/^\-\-\- .+/, "--- expected")
-
result.sub!(/^\+\+\+ .+/, "+++ actual")
-
-
if result.empty? then
-
klass = exp.class
-
result = [
-
"No visible difference in the #{klass}#inspect output.",
-
"You should look at your implementation of #{klass}#==.",
-
expect
-
].join "\n"
-
end
-
end
-
end
-
-
result
-
end
-
-
##
-
# This returns a human-readable version of +obj+. By default
-
# #inspect is called. You can override this to use #pretty_print
-
# if you want.
-
-
1
def mu_pp obj
-
45
s = obj.inspect
-
45
s = s.encode Encoding.default_external if defined? Encoding
-
45
s
-
end
-
-
##
-
# This returns a diff-able human-readable version of +obj+. This
-
# differs from the regular mu_pp because it expands escaped
-
# newlines and makes hex-values generic (like object_ids). This
-
# uses mu_pp to do the first pass and then cleans it up.
-
-
1
def mu_pp_for_diff obj # TODO: possibly rename
-
mu_pp(obj).gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX')
-
end
-
-
1
def _assertions= n # :nodoc:
-
2116
@_assertions = n
-
end
-
-
1
def _assertions # :nodoc:
-
2726
@_assertions ||= 0
-
end
-
-
##
-
# Fails unless +test+ is a true value.
-
-
1
def assert test, msg = nil
-
1506
msg ||= "Failed assertion, no message given."
-
1506
self._assertions += 1
-
1506
unless test then
-
msg = msg.call if Proc === msg
-
raise MiniTest::Assertion, msg
-
end
-
1506
true
-
end
-
-
##
-
# Fails unless the block returns a true value.
-
#
-
# NOTE: This method is deprecated, use assert. It will be removed
-
# on 2013-01-01."
-
-
1
def assert_block msg = nil
-
warn "NOTE: MiniTest::Unit::TestCase#assert_block is deprecated, use assert. It will be removed on 2013-01-01. Called from #{caller.first}"
-
msg = message(msg) { "Expected block to return true value" }
-
assert yield, msg
-
end
-
-
##
-
# Fails unless +obj+ is empty.
-
-
1
def assert_empty obj, msg = nil
-
1
msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
-
1
assert_respond_to obj, :empty?
-
1
assert obj.empty?, msg
-
end
-
-
##
-
# Fails unless <tt>exp == act</tt> printing the difference between
-
# the two, if possible.
-
#
-
# If there is no visible difference but the assertion fails, you
-
# should suspect that your #== is buggy, or your inspect output is
-
# missing crucial details.
-
#
-
# For floats use assert_in_delta.
-
#
-
# See also: MiniTest::Assertions.diff
-
-
1
def assert_equal exp, act, msg = nil
-
481
msg = message(msg, "") { diff exp, act }
-
481
assert(exp == act, msg)
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
-
# of each other.
-
#
-
# assert_in_delta Math::PI, (22.0 / 7.0), 0.01
-
-
1
def assert_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) { "Expected |#{exp} - #{act}| (#{n}) to be < #{delta}"}
-
assert delta >= n, msg
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ have a relative
-
# error less than +epsilon+.
-
-
1
def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
-
assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
-
end
-
-
##
-
# Fails unless +collection+ includes +obj+.
-
-
1
def assert_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
assert collection.include?(obj), msg
-
end
-
-
##
-
# Fails unless +obj+ is an instance of +cls+.
-
-
1
def assert_instance_of cls, obj, msg = nil
-
1
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
-
}
-
-
1
assert obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails unless +obj+ is a kind of +cls+.
-
-
1
def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
-
4
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
-
-
4
assert obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails unless +matcher+ <tt>=~</tt> +obj+.
-
-
1
def assert_match matcher, obj, msg = nil
-
84
msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
-
84
assert_respond_to matcher, :"=~"
-
84
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
84
assert matcher =~ obj, msg
-
end
-
-
##
-
# Fails unless +obj+ is nil
-
-
1
def assert_nil obj, msg = nil
-
4
msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
-
4
assert obj.nil?, msg
-
end
-
-
##
-
# For testing with binary operators.
-
#
-
# assert_operator 5, :<=, 4
-
-
1
def assert_operator o1, op, o2 = UNDEFINED, msg = nil
-
return assert_predicate o1, op, msg if UNDEFINED == o2
-
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
-
assert o1.__send__(op, o2), msg
-
end
-
-
##
-
# Fails if stdout or stderr do not output the expected results.
-
# Pass in nil if you don't care about that streams output. Pass in
-
# "" if you require it to be silent. Pass in a regexp if you want
-
# to pattern match.
-
#
-
# NOTE: this uses #capture_io, not #capture_subprocess_io.
-
#
-
# See also: #assert_silent
-
-
1
def assert_output stdout = nil, stderr = nil
-
out, err = capture_io do
-
yield
-
end
-
-
err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
-
out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
-
-
y = send err_msg, stderr, err, "In stderr" if err_msg
-
x = send out_msg, stdout, out, "In stdout" if out_msg
-
-
(!stdout || x) && (!stderr || y)
-
end
-
-
##
-
# For testing with predicates.
-
#
-
# assert_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by assert_operator:
-
#
-
# str.must_be :empty?
-
-
1
def assert_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
-
assert o1.__send__(op), msg
-
end
-
-
##
-
# Fails unless the block raises one of +exp+. Returns the
-
# exception matched so you can check the message, attributes, etc.
-
-
1
def assert_raises *exp
-
45
msg = "#{exp.pop}.\n" if String === exp.last
-
-
45
should_raise = false
-
45
begin
-
45
yield
-
should_raise = true
-
rescue MiniTest::Skip => e
-
details = "#{msg}#{mu_pp(exp)} exception expected, not"
-
-
if exp.include? MiniTest::Skip then
-
return e
-
else
-
raise e
-
end
-
rescue Exception => e
-
45
details = "#{msg}#{mu_pp(exp)} exception expected, not"
-
assert(exp.any? { |ex|
-
45
ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class
-
45
}, exception_details(e, details))
-
-
45
return e
-
end
-
-
exp = exp.first if exp.size == 1
-
flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." if
-
should_raise
-
end
-
-
##
-
# Fails unless +obj+ responds to +meth+.
-
-
1
def assert_respond_to obj, meth, msg = nil
-
104
msg = message(msg) {
-
"Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
-
}
-
104
assert obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails unless +exp+ and +act+ are #equal?
-
-
1
def assert_same exp, act, msg = nil
-
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
-
}
-
assert exp.equal?(act), msg
-
end
-
-
##
-
# +send_ary+ is a receiver, message and arguments.
-
#
-
# Fails unless the call returns a true value
-
# TODO: I should prolly remove this from specs
-
-
1
def assert_send send_ary, m = nil
-
recv, msg, *args = send_ary
-
m = message(m) {
-
"Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
-
assert recv.__send__(msg, *args), m
-
end
-
-
##
-
# Fails if the block outputs anything to stderr or stdout.
-
#
-
# See also: #assert_output
-
-
1
def assert_silent
-
assert_output "", "" do
-
yield
-
end
-
end
-
-
##
-
# Fails unless the block throws +sym+
-
-
1
def assert_throws sym, msg = nil
-
default = "Expected #{mu_pp(sym)} to have been thrown"
-
caught = true
-
catch(sym) do
-
begin
-
yield
-
rescue ThreadError => e # wtf?!? 1.8 + threads == suck
-
default += ", not :#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
-
rescue ArgumentError => e # 1.9 exception
-
default += ", not #{e.message.split(/ /).last}"
-
rescue NameError => e # 1.8 exception
-
default += ", not #{e.name.inspect}"
-
end
-
caught = false
-
end
-
-
assert caught, message(msg) { default }
-
end
-
-
##
-
# Captures $stdout and $stderr into strings:
-
#
-
# out, err = capture_io do
-
# puts "Some info"
-
# warn "You did a bad thing"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: For efficiency, this method uses StringIO and does not
-
# capture IO for subprocesses. Use #capture_subprocess_io for
-
# that.
-
-
1
def capture_io
-
require 'stringio'
-
-
captured_stdout, captured_stderr = StringIO.new, StringIO.new
-
-
synchronize do
-
orig_stdout, orig_stderr = $stdout, $stderr
-
$stdout, $stderr = captured_stdout, captured_stderr
-
-
begin
-
yield
-
ensure
-
$stdout = orig_stdout
-
$stderr = orig_stderr
-
end
-
end
-
-
return captured_stdout.string, captured_stderr.string
-
end
-
-
##
-
# Captures $stdout and $stderr into strings, using Tempfile to
-
# ensure that subprocess IO is captured as well.
-
#
-
# out, err = capture_subprocess_io do
-
# system "echo Some info"
-
# system "echo You did a bad thing 1>&2"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: This method is approximately 10x slower than #capture_io so
-
# only use it when you need to test the output of a subprocess.
-
-
1
def capture_subprocess_io
-
require 'tempfile'
-
-
captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
-
-
synchronize do
-
orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
-
$stdout.reopen captured_stdout
-
$stderr.reopen captured_stderr
-
-
begin
-
yield
-
-
$stdout.rewind
-
$stderr.rewind
-
-
[captured_stdout.read, captured_stderr.read]
-
ensure
-
captured_stdout.unlink
-
captured_stderr.unlink
-
$stdout.reopen orig_stdout
-
$stderr.reopen orig_stderr
-
end
-
end
-
end
-
-
##
-
# Returns details for exception +e+
-
-
1
def exception_details e, msg
-
[
-
45
"#{msg}",
-
"Class: <#{e.class}>",
-
"Message: <#{e.message.inspect}>",
-
"---Backtrace---",
-
"#{MiniTest::filter_backtrace(e.backtrace).join("\n")}",
-
"---------------",
-
].join "\n"
-
end
-
-
##
-
# Fails with +msg+
-
-
1
def flunk msg = nil
-
msg ||= "Epic Fail!"
-
assert false, msg
-
end
-
-
##
-
# Returns a proc that will output +msg+ along with the default message.
-
-
1
def message msg = nil, ending = ".", &default
-
695
proc {
-
custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
-
"#{custom_message}#{default.call}#{ending}"
-
}
-
end
-
-
##
-
# used for counting assertions
-
-
1
def pass msg = nil
-
assert true
-
end
-
-
##
-
# Fails if +test+ is a true value
-
-
1
def refute test, msg = nil
-
16
msg ||= "Failed refutation, no message given"
-
16
not assert(! test, msg)
-
end
-
-
##
-
# Fails if +obj+ is empty.
-
-
1
def refute_empty obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
-
assert_respond_to obj, :empty?
-
refute obj.empty?, msg
-
end
-
-
##
-
# Fails if <tt>exp == act</tt>.
-
#
-
# For floats use refute_in_delta.
-
-
1
def refute_equal exp, act, msg = nil
-
1
msg = message(msg) {
-
"Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
-
}
-
1
refute exp == act, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
-
#
-
# refute_in_delta Math::PI, (22.0 / 7.0)
-
-
1
def refute_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) {
-
"Expected |#{exp} - #{act}| (#{n}) to not be < #{delta}"
-
}
-
refute delta > n, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ and +act+ have a relative error
-
# less than +epsilon+.
-
-
1
def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
-
refute_in_delta a, b, a * epsilon, msg
-
end
-
-
##
-
# Fails if +collection+ includes +obj+.
-
-
1
def refute_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
refute collection.include?(obj), msg
-
end
-
-
##
-
# Fails if +obj+ is an instance of +cls+.
-
-
1
def refute_instance_of cls, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to not be an instance of #{cls}"
-
}
-
refute obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails if +obj+ is a kind of +cls+.
-
-
1
def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
-
refute obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails if +matcher+ <tt>=~</tt> +obj+.
-
-
1
def refute_match matcher, obj, msg = nil
-
12
msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
-
12
assert_respond_to matcher, :"=~"
-
12
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
12
refute matcher =~ obj, msg
-
end
-
-
##
-
# Fails if +obj+ is nil.
-
-
1
def refute_nil obj, msg = nil
-
2
msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
-
2
refute obj.nil?, msg
-
end
-
-
##
-
# Fails if +o1+ is not +op+ +o2+. Eg:
-
#
-
# refute_operator 1, :>, 2 #=> pass
-
# refute_operator 1, :<, 2 #=> fail
-
-
1
def refute_operator o1, op, o2 = UNDEFINED, msg = nil
-
return refute_predicate o1, op, msg if UNDEFINED == o2
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
-
refute o1.__send__(op, o2), msg
-
end
-
-
##
-
# For testing with predicates.
-
#
-
# refute_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by refute_operator:
-
#
-
# str.wont_be :empty?
-
-
1
def refute_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
-
refute o1.__send__(op), msg
-
end
-
-
##
-
# Fails if +obj+ responds to the message +meth+.
-
-
1
def refute_respond_to obj, meth, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
-
-
refute obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails if +exp+ is the same (by object identity) as +act+.
-
-
1
def refute_same exp, act, msg = nil
-
1
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
-
}
-
1
refute exp.equal?(act), msg
-
end
-
-
##
-
# Skips the current test. Gets listed at the end of the run but
-
# doesn't cause a failure exit code.
-
-
1
def skip msg = nil, bt = caller
-
msg ||= "Skipped, no message given"
-
raise MiniTest::Skip, msg, bt
-
end
-
-
##
-
# Takes a block and wraps it with the runner's shared mutex.
-
-
1
def synchronize
-
Minitest::Unit.runner.synchronize do
-
yield
-
end
-
end
-
end
-
-
1
class Unit # :nodoc:
-
1
VERSION = "4.2.0" # :nodoc:
-
-
1
attr_accessor :report, :failures, :errors, :skips # :nodoc:
-
1
attr_accessor :test_count, :assertion_count # :nodoc:
-
1
attr_accessor :start_time # :nodoc:
-
1
attr_accessor :help # :nodoc:
-
1
attr_accessor :verbose # :nodoc:
-
1
attr_writer :options # :nodoc:
-
-
##
-
# Lazy accessor for options.
-
-
1
def options
-
42
@options ||= {}
-
end
-
-
1
@@installed_at_exit ||= false
-
1
@@out = $stdout
-
1
@@after_tests = []
-
-
##
-
# A simple hook allowing you to run a block of code after _all_ of
-
# the tests are done. Eg:
-
#
-
# MiniTest::Unit.after_tests { p $debugging_info }
-
-
1
def self.after_tests &block
-
@@after_tests << block
-
end
-
-
##
-
# Registers MiniTest::Unit to run tests at process exit
-
-
1
def self.autorun
-
at_exit {
-
1
next if $! # don't run if there was an exception
-
-
# the order here is important. The at_exit handler must be
-
# installed before anyone else gets a chance to install their
-
# own, that way we can be assured that our exit will be last
-
# to run (at_exit stacks).
-
1
exit_code = nil
-
-
1
at_exit {
-
1
@@after_tests.reverse_each(&:call)
-
1
exit false if exit_code && exit_code != 0
-
}
-
-
1
exit_code = MiniTest::Unit.new.run ARGV
-
1
} unless @@installed_at_exit
-
1
@@installed_at_exit = true
-
end
-
-
##
-
# Returns the stream to use for output.
-
-
1
def self.output
-
623
@@out
-
end
-
-
##
-
# Sets MiniTest::Unit to write output to +stream+. $stdout is the default
-
# output
-
-
1
def self.output= stream
-
@@out = stream
-
end
-
-
##
-
# Tells MiniTest::Unit to delegate to +runner+, an instance of a
-
# MiniTest::Unit subclass, when MiniTest::Unit#run is called.
-
-
1
def self.runner= runner
-
@@runner = runner
-
end
-
-
##
-
# Returns the MiniTest::Unit subclass instance that will be used
-
# to run the tests. A MiniTest::Unit instance is the default
-
# runner.
-
-
1
def self.runner
-
1
@@runner ||= self.new
-
end
-
-
##
-
# Return all plugins' run methods (methods that start with "run_").
-
-
1
def self.plugins
-
@@plugins ||= (["run_tests"] +
-
public_instance_methods(false).
-
2
grep(/^run_/).map { |s| s.to_s }).uniq
-
end
-
-
##
-
# Return the IO for output.
-
-
1
def output
-
623
self.class.output
-
end
-
-
1
def puts *a # :nodoc:
-
8
output.puts(*a)
-
end
-
-
1
def print *a # :nodoc:
-
610
output.print(*a)
-
end
-
-
##
-
# Runner for a given +type+ (eg, test vs bench).
-
-
1
def _run_anything type
-
1
suites = TestCase.send "#{type}_suites"
-
1
return if suites.empty?
-
-
1
start = Time.now
-
-
1
puts
-
1
puts "# Running #{type}s:"
-
1
puts
-
-
1
@test_count, @assertion_count = 0, 0
-
1
sync = output.respond_to? :"sync=" # stupid emacs
-
1
old_sync, output.sync = output.sync, true if sync
-
-
1
results = _run_suites suites, type
-
-
43
@test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
-
43
@assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
-
-
1
output.sync = old_sync if sync
-
-
1
t = Time.now - start
-
-
1
puts
-
1
puts
-
puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
-
1
[t, test_count / t, assertion_count / t]
-
-
1
report.each_with_index do |msg, i|
-
puts "\n%3d) %s" % [i + 1, msg]
-
end
-
-
1
puts
-
-
1
status
-
end
-
-
##
-
# Runs all the +suites+ for a given +type+. Runs suites declaring
-
# a test_order of +:parallel+ in parallel, and everything else
-
# serial.
-
-
1
def _run_suites suites, type
-
43
parallel, serial = suites.partition { |s| s.test_order == :parallel }
-
-
ParallelEach.new(parallel).map { |suite| _run_suite suite, type } +
-
43
serial.map { |suite| _run_suite suite, type }
-
end
-
-
##
-
# Run a single +suite+ for a given +type+.
-
-
1
def _run_suite suite, type
-
42
header = "#{type}_suite_header"
-
42
puts send(header, suite) if respond_to? header
-
-
42
filter = options[:filter] || '/./'
-
42
filter = Regexp.new $1 if filter =~ /\/(.*)\//
-
-
42
assertions = suite.send("#{type}_methods").grep(filter).map { |method|
-
610
inst = suite.new method
-
610
inst._assertions = 0
-
-
610
print "#{suite}##{method} = " if @verbose
-
-
610
start_time = Time.now if @verbose
-
610
result = inst.run self
-
-
610
print "%.2f s = " % (Time.now - start_time) if @verbose
-
610
print result
-
610
puts if @verbose
-
-
610
inst._assertions
-
}
-
-
652
return assertions.size, assertions.inject(0) { |sum, n| sum + n }
-
end
-
-
##
-
# Record the result of a single run. Makes it very easy to gather
-
# information. Eg:
-
#
-
# class StatisticsRecorder < MiniTest::Unit
-
# def record suite, method, assertions, time, error
-
# # ... record the results somewhere ...
-
# end
-
# end
-
#
-
# MiniTest::Unit.runner = StatisticsRecorder.new
-
-
1
def record suite, method, assertions, time, error
-
end
-
-
1
def location e # :nodoc:
-
last_before_assertion = ""
-
e.backtrace.reverse_each do |s|
-
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
-
last_before_assertion = s
-
end
-
last_before_assertion.sub(/:in .*$/, '')
-
end
-
-
##
-
# Writes status for failed test +meth+ in +klass+ which finished with
-
# exception +e+
-
-
1
def puke klass, meth, e
-
e = case e
-
when MiniTest::Skip then
-
@skips += 1
-
return "S" unless @verbose
-
"Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
-
when MiniTest::Assertion then
-
@failures += 1
-
"Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
-
else
-
@errors += 1
-
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
-
"Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
-
end
-
@report << e
-
e[0, 1]
-
end
-
-
1
def initialize # :nodoc:
-
2
@report = []
-
2
@errors = @failures = @skips = 0
-
2
@verbose = false
-
2
@mutex = Mutex.new
-
end
-
-
1
def synchronize # :nodoc:
-
@mutex.synchronize { yield }
-
end
-
-
1
def process_args args = [] # :nodoc:
-
1
options = {}
-
1
orig_args = args.dup
-
-
1
OptionParser.new do |opts|
-
1
opts.banner = 'minitest options:'
-
1
opts.version = MiniTest::Unit::VERSION
-
-
1
opts.on '-h', '--help', 'Display this help.' do
-
puts opts
-
exit
-
end
-
-
1
opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
-
options[:seed] = m.to_i
-
end
-
-
1
opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
-
options[:verbose] = true
-
end
-
-
1
opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
-
options[:filter] = a
-
end
-
-
1
opts.parse! args
-
1
orig_args -= args
-
end
-
-
1
unless options[:seed] then
-
1
srand
-
1
options[:seed] = srand % 0xFFFF
-
1
orig_args << "--seed" << options[:seed].to_s
-
end
-
-
1
srand options[:seed]
-
-
1
self.verbose = options[:verbose]
-
3
@help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
-
-
1
options
-
end
-
-
##
-
# Begins the full test run. Delegates to +runner+'s #_run method.
-
-
1
def run args = []
-
1
self.class.runner._run(args)
-
end
-
-
##
-
# Top level driver, controls all output and filtering.
-
-
1
def _run args = []
-
1
self.options = process_args args
-
-
1
puts "Run options: #{help}"
-
-
1
self.class.plugins.each do |plugin|
-
1
send plugin
-
1
break unless report.empty?
-
end
-
-
1
return failures + errors if @test_count > 0 # or return nil...
-
rescue Interrupt
-
abort 'Interrupted'
-
end
-
-
##
-
# Runs test suites matching +filter+.
-
-
1
def run_tests
-
1
_run_anything :test
-
end
-
-
##
-
# Writes status to +io+
-
-
1
def status io = self.output
-
1
format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
-
1
io.puts format % [test_count, assertion_count, failures, errors, skips]
-
end
-
-
##
-
# Provides a simple set of guards that you can use in your tests
-
# to skip execution if it is not applicable. These methods are
-
# mixed into TestCase as both instance and class methods so you
-
# can use them inside or outside of the test methods.
-
#
-
# def test_something_for_mri
-
# skip "bug 1234" if jruby?
-
# # ...
-
# end
-
#
-
# if windows? then
-
# # ... lots of test methods ...
-
# end
-
-
1
module Guard
-
-
##
-
# Is this running on jruby?
-
-
1
def jruby? platform = RUBY_PLATFORM
-
"java" == platform
-
end
-
-
##
-
# Is this running on mri?
-
-
1
def mri? platform = RUBY_DESCRIPTION
-
/^ruby/ =~ platform
-
end
-
-
##
-
# Is this running on rubinius?
-
-
1
def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
-
"rbx" == platform
-
end
-
-
##
-
# Is this running on windows?
-
-
1
def windows? platform = RUBY_PLATFORM
-
/mswin|mingw/ =~ platform
-
end
-
end
-
-
##
-
# Provides before/after hooks for setup and teardown. These are
-
# meant for library writers, NOT for regular test authors. See
-
# #before_setup for an example.
-
-
1
module LifecycleHooks
-
##
-
# Runs before every test, after setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_setup; end
-
-
##
-
# Runs before every test, before setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# As a simplistic example:
-
#
-
# module MyMinitestPlugin
-
# def before_setup
-
# super
-
# # ... stuff to do before setup is run
-
# end
-
#
-
# def after_setup
-
# # ... stuff to do after setup is run
-
# super
-
# end
-
#
-
# def before_teardown
-
# super
-
# # ... stuff to do before teardown is run
-
# end
-
#
-
# def after_teardown
-
# # ... stuff to do after teardown is run
-
# super
-
# end
-
# end
-
#
-
# class MiniTest::Unit::TestCase
-
# include MyMinitestPlugin
-
# end
-
-
1
def before_setup; end
-
-
##
-
# Runs after every test, before teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def before_teardown; end
-
-
##
-
# Runs after every test, after teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_teardown; end
-
end
-
-
1
module Deprecated # :nodoc:
-
-
##
-
# This entire module is deprecated and slated for removal on 2013-01-01.
-
-
1
module Hooks
-
1
def run_setup_hooks # :nodoc:
-
_run_hooks self.class.setup_hooks
-
end
-
-
1
def _run_hooks hooks # :nodoc:
-
hooks.each do |hook|
-
if hook.respond_to?(:arity) && hook.arity == 1
-
hook.call(self)
-
else
-
hook.call
-
end
-
end
-
end
-
-
1
def run_teardown_hooks # :nodoc:
-
_run_hooks self.class.teardown_hooks.reverse
-
end
-
end
-
-
##
-
# This entire module is deprecated and slated for removal on 2013-01-01.
-
-
1
module HooksCM
-
##
-
# Adds a block of code that will be executed before every
-
# TestCase is run.
-
#
-
# NOTE: This method is deprecated, use before/after_setup. It
-
# will be removed on 2013-01-01.
-
-
1
def add_setup_hook arg=nil, &block
-
warn "NOTE: MiniTest::Unit::TestCase.add_setup_hook is deprecated, use before/after_setup via a module (and call super!). It will be removed on 2013-01-01. Called from #{caller.first}"
-
hook = arg || block
-
@setup_hooks << hook
-
end
-
-
1
def setup_hooks # :nodoc:
-
if superclass.respond_to? :setup_hooks then
-
superclass.setup_hooks
-
else
-
[]
-
end + @setup_hooks
-
end
-
-
##
-
# Adds a block of code that will be executed after every
-
# TestCase is run.
-
#
-
# NOTE: This method is deprecated, use before/after_teardown. It
-
# will be removed on 2013-01-01.
-
-
1
def add_teardown_hook arg=nil, &block
-
warn "NOTE: MiniTest::Unit::TestCase#add_teardown_hook is deprecated, use before/after_teardown. It will be removed on 2013-01-01. Called from #{caller.first}"
-
hook = arg || block
-
@teardown_hooks << hook
-
end
-
-
1
def teardown_hooks # :nodoc:
-
if superclass.respond_to? :teardown_hooks then
-
superclass.teardown_hooks
-
else
-
[]
-
end + @teardown_hooks
-
end
-
end
-
end
-
-
##
-
# Subclass TestCase to create your own tests. Typically you'll want a
-
# TestCase subclass per implementation class.
-
#
-
# See MiniTest::Assertions
-
-
1
class TestCase
-
1
include LifecycleHooks
-
1
include Deprecated::Hooks
-
1
extend Deprecated::HooksCM # UGH... I can't wait 'til 2013!
-
1
include Guard
-
1
extend Guard
-
-
1
attr_reader :__name__ # :nodoc:
-
-
1
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException,
-
Interrupt, SystemExit] # :nodoc:
-
-
1
SUPPORTS_INFO_SIGNAL = Signal.list['INFO'] # :nodoc:
-
-
##
-
# Runs the tests reporting the status to +runner+
-
-
1
def run runner
-
trap "INFO" do
-
runner.report.each_with_index do |msg, i|
-
warn "\n%3d) %s" % [i + 1, msg]
-
end
-
warn ''
-
time = runner.start_time ? Time.now - runner.start_time : 0
-
warn "Current Test: %s#%s %.2fs" % [self.class, self.__name__, time]
-
runner.status $stderr
-
610
end if SUPPORTS_INFO_SIGNAL
-
-
610
start_time = Time.now
-
-
610
result = ""
-
610
begin
-
610
@passed = nil
-
610
self.before_setup
-
610
self.setup
-
610
self.after_setup
-
610
self.run_test self.__name__
-
610
result = "." unless io?
-
610
time = Time.now - start_time
-
610
runner.record self.class, self.__name__, self._assertions, time, nil
-
610
@passed = true
-
rescue *PASSTHROUGH_EXCEPTIONS
-
raise
-
rescue Exception => e
-
@passed = false
-
time = Time.now - start_time
-
runner.record self.class, self.__name__, self._assertions, time, e
-
result = runner.puke self.class, self.__name__, e
-
ensure
-
610
%w{ before_teardown teardown after_teardown }.each do |hook|
-
1830
begin
-
1830
self.send hook
-
rescue *PASSTHROUGH_EXCEPTIONS
-
raise
-
rescue Exception => e
-
@passed = false
-
result = runner.puke self.class, self.__name__, e
-
end
-
end
-
610
trap 'INFO', 'DEFAULT' if SUPPORTS_INFO_SIGNAL
-
end
-
610
result
-
end
-
-
1
alias :run_test :__send__
-
-
1
def initialize name # :nodoc:
-
610
@__name__ = name
-
610
@__io__ = nil
-
610
@passed = nil
-
610
@@current = self
-
end
-
-
1
def self.current # :nodoc:
-
@@current
-
end
-
-
##
-
# Return the output IO object
-
-
1
def io
-
@__io__ = true
-
MiniTest::Unit.output
-
end
-
-
##
-
# Have we hooked up the IO yet?
-
-
1
def io?
-
610
@__io__
-
end
-
-
1
def self.reset # :nodoc:
-
1
@@test_suites = {}
-
end
-
-
1
reset
-
-
##
-
# Call this at the top of your tests when you absolutely
-
# positively need to have ordered tests. In doing so, you're
-
# admitting that you suck and your tests are weak.
-
-
1
def self.i_suck_and_my_tests_are_order_dependent!
-
class << self
-
undef_method :test_order if method_defined? :test_order
-
define_method :test_order do :alpha end
-
end
-
end
-
-
##
-
# Make diffs for this TestCase use #pretty_inspect so that diff
-
# in assert_equal can be more details. NOTE: this is much slower
-
# than the regular inspect but much more usable for complex
-
# objects.
-
-
1
def self.make_my_diffs_pretty!
-
require 'pp'
-
-
define_method :mu_pp do |o|
-
o.pretty_inspect
-
end
-
end
-
-
##
-
# Call this at the top of your tests when you want to run your
-
# tests in parallel. In doing so, you're admitting that you rule
-
# and your tests are awesome.
-
-
1
def self.parallelize_me!
-
class << self
-
undef_method :test_order if method_defined? :test_order
-
define_method :test_order do :parallel end
-
end
-
end
-
-
1
def self.inherited klass # :nodoc:
-
42
@@test_suites[klass] = true
-
42
klass.reset_setup_teardown_hooks
-
42
super
-
end
-
-
1
def self.test_order # :nodoc:
-
2
:random
-
end
-
-
1
def self.test_suites # :nodoc:
-
43
@@test_suites.keys.sort_by { |ts| ts.name.to_s }
-
end
-
-
1
def self.test_methods # :nodoc:
-
652
methods = public_instance_methods(true).grep(/^test/).map { |m| m.to_s }
-
-
42
case self.test_order
-
when :parallel
-
max = methods.size
-
ParallelEach.new methods.sort.sort_by { rand max }
-
when :random then
-
1
max = methods.size
-
1
methods.sort.sort_by { rand max }
-
when :alpha, :sorted then
-
41
methods.sort
-
else
-
raise "Unknown test_order: #{self.test_order.inspect}"
-
end
-
end
-
-
##
-
# Returns true if the test passed.
-
-
1
def passed?
-
@passed
-
end
-
-
##
-
# Runs before every test. Use this to set up before each test
-
# run.
-
-
1
def setup; end
-
-
##
-
# Runs after every test. Use this to clean up after each test
-
# run.
-
-
1
def teardown; end
-
-
1
def self.reset_setup_teardown_hooks # :nodoc:
-
# also deprecated... believe it.
-
43
@setup_hooks = []
-
43
@teardown_hooks = []
-
end
-
-
1
reset_setup_teardown_hooks
-
-
1
include MiniTest::Assertions
-
end # class TestCase
-
end # class Unit
-
end # module MiniTest
-
-
1
Minitest = MiniTest # :nodoc: because ugh... I typo this all the time
-
-
1
if $DEBUG then
-
module Test # :nodoc:
-
module Unit # :nodoc:
-
class TestCase # :nodoc:
-
def self.inherited x # :nodoc:
-
# this helps me ferret out porting issues
-
raise "Using minitest and test/unit in the same process: #{x}"
-
end
-
end
-
end
-
end
-
end
-
1
module MultiJson
-
1
module Adapters
-
1
module JsonCommon
-
-
1
def load(string, options={})
-
3
string = string.read if string.respond_to?(:read)
-
3
::JSON.parse(string, :symbolize_names => options[:symbolize_keys])
-
end
-
-
1
def dump(object, options={})
-
object.to_json(process_options(options))
-
end
-
-
1
protected
-
-
1
def process_options(options={})
-
return options if options.empty?
-
opts = {}
-
opts.merge!(JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
-
opts.merge!(options)
-
end
-
-
end
-
end
-
end
-
1
require 'json' unless defined?(::JSON)
-
1
require 'multi_json/adapters/json_common'
-
-
1
module MultiJson
-
1
module Adapters
-
# Use the JSON gem to dump/load.
-
1
class JsonGem
-
1
ParseError = ::JSON::ParserError
-
1
extend JsonCommon
-
end
-
end
-
end
-
# Define a package task library to aid in the definition of
-
# redistributable package files.
-
-
1
require 'rake'
-
1
require 'rake/tasklib'
-
-
1
module Rake
-
-
# Create a packaging task that will package the project into
-
# distributable files (e.g zip archive or tar files).
-
#
-
# The PackageTask will create the following targets:
-
#
-
# [<b>:package</b>]
-
# Create all the requested package files.
-
#
-
# [<b>:clobber_package</b>]
-
# Delete all the package files. This target is automatically
-
# added to the main clobber target.
-
#
-
# [<b>:repackage</b>]
-
# Rebuild the package files from scratch, even if they are not out
-
# of date.
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tgz"</b>]
-
# Create a gzipped tar package (if <em>need_tar</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.gz"</b>]
-
# Create a gzipped tar package (if <em>need_tar_gz</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.bz2"</b>]
-
# Create a bzip2'd tar package (if <em>need_tar_bz2</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.zip"</b>]
-
# Create a zip package archive (if <em>need_zip</em> is true).
-
#
-
# Example:
-
#
-
# Rake::PackageTask.new("rake", "1.2.3") do |p|
-
# p.need_tar = true
-
# p.package_files.include("lib/**/*.rb")
-
# end
-
#
-
1
class PackageTask < TaskLib
-
# Name of the package (from the GEM Spec).
-
1
attr_accessor :name
-
-
# Version of the package (e.g. '1.3.2').
-
1
attr_accessor :version
-
-
# Directory used to store the package files (default is 'pkg').
-
1
attr_accessor :package_dir
-
-
# True if a gzipped tar file (tgz) should be produced (default is false).
-
1
attr_accessor :need_tar
-
-
# True if a gzipped tar file (tar.gz) should be produced (default is false).
-
1
attr_accessor :need_tar_gz
-
-
# True if a bzip2'd tar file (tar.bz2) should be produced (default is false).
-
1
attr_accessor :need_tar_bz2
-
-
# True if a zip file should be produced (default is false)
-
1
attr_accessor :need_zip
-
-
# List of files to be included in the package.
-
1
attr_accessor :package_files
-
-
# Tar command for gzipped or bzip2ed archives. The default is 'tar'.
-
1
attr_accessor :tar_command
-
-
# Zip command for zipped archives. The default is 'zip'.
-
1
attr_accessor :zip_command
-
-
# Create a Package Task with the given name and version. Use +:noversion+
-
# as the version to build a package without a version or to provide a
-
# fully-versioned package name.
-
-
1
def initialize(name=nil, version=nil)
-
init(name, version)
-
yield self if block_given?
-
define unless name.nil?
-
end
-
-
# Initialization that bypasses the "yield self" and "define" step.
-
1
def init(name, version)
-
1
@name = name
-
1
@version = version
-
1
@package_files = Rake::FileList.new
-
1
@package_dir = 'pkg'
-
1
@need_tar = false
-
1
@need_tar_gz = false
-
1
@need_tar_bz2 = false
-
1
@need_zip = false
-
1
@tar_command = 'tar'
-
1
@zip_command = 'zip'
-
end
-
-
# Create the tasks defined by this task library.
-
1
def define
-
1
fail "Version required (or :noversion)" if @version.nil?
-
1
@version = nil if :noversion == @version
-
-
1
desc "Build all the packages"
-
1
task :package
-
-
1
desc "Force a rebuild of the package files"
-
1
task :repackage => [:clobber_package, :package]
-
-
1
desc "Remove package products"
-
1
task :clobber_package do
-
rm_r package_dir rescue nil
-
end
-
-
1
task :clobber => [:clobber_package]
-
-
[
-
1
[need_tar, tgz_file, "z"],
-
[need_tar_gz, tar_gz_file, "z"],
-
[need_tar_bz2, tar_bz2_file, "j"]
-
].each do |(need, file, flag)|
-
3
if need
-
task :package => ["#{package_dir}/#{file}"]
-
file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
-
chdir(package_dir) do
-
sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
-
end
-
end
-
end
-
end
-
-
1
if need_zip
-
task :package => ["#{package_dir}/#{zip_file}"]
-
file "#{package_dir}/#{zip_file}" => [package_dir_path] + package_files do
-
chdir(package_dir) do
-
sh %{#{@zip_command} -r #{zip_file} #{package_name}}
-
end
-
end
-
end
-
-
1
directory package_dir
-
-
1
file package_dir_path => @package_files do
-
mkdir_p package_dir rescue nil
-
@package_files.each do |fn|
-
f = File.join(package_dir_path, fn)
-
fdir = File.dirname(f)
-
mkdir_p(fdir) if !File.exist?(fdir)
-
if File.directory?(fn)
-
mkdir_p(f)
-
else
-
rm_f f
-
safe_ln(fn, f)
-
end
-
end
-
end
-
1
self
-
end
-
-
1
def package_name
-
4
@version ? "#{@name}-#{@version}" : @name
-
end
-
-
1
def package_dir_path
-
1
"#{package_dir}/#{package_name}"
-
end
-
-
1
def tgz_file
-
1
"#{package_name}.tgz"
-
end
-
-
1
def tar_gz_file
-
1
"#{package_name}.tar.gz"
-
end
-
-
1
def tar_bz2_file
-
1
"#{package_name}.tar.bz2"
-
end
-
-
1
def zip_file
-
"#{package_name}.zip"
-
end
-
end
-
-
end
-
1
require 'rake'
-
-
1
module Rake
-
-
# Base class for Task Libraries.
-
1
class TaskLib
-
1
include Cloneable
-
1
include Rake::DSL
-
-
# Make a symbol by pasting two strings together.
-
#
-
# NOTE: DEPRECATED! This method is kinda stupid. I don't know why
-
# I didn't just use string interpolation. But now other task
-
# libraries depend on this so I can't remove it without breaking
-
# other people's code. So for now it stays for backwards
-
# compatibility. BUT DON'T USE IT.
-
1
def paste(a,b) # :nodoc:
-
(a.to_s + b.to_s).intern
-
end
-
end
-
-
end